预处理详解

目录

1、预定义符号

2、#define定义常量

3、#define定义宏

4、带有副作用的宏参数

5、宏的替换规则

6、宏和函数的对比

​ 7、#和##

8、命名约定

9、#undef

10、条件编译

11、头文件的包含方式

12、嵌套文件包含


1、预定义符号

C语言设置了一些预定义符号,可以在预处理阶段直接使用:

1 FILE //进行编译的源文件

2 LINE //文件当前的行号

3 DATE //文件被编译的日期

4 TIME //文件被编译的时间

5 STDC //如果编译器遵循ANSI C,其值为1,否则未定义

比如:

2、#define定义常量

这个比较简单,和我们的用法差不多,

#define MAX 100

#define REG register

当我们定义的语句比较长,可以分成多行来写,比如:

#define DEBUG_PRINT printf("file:%s\tline:%d\t \

date:%s\ttime:%s\n" ,\

FILE,LINE , \

DATE,TIME )

在每行的末尾处加上反斜杠 \ ,也叫续行符,最后一行不用加

在定义#define时,最后不用加 ; 容易出现语法错误,

3、#define定义宏

#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常成为宏或者定义宏

宏的声明方式:

#define name( parament-list ) stuff

其中parament-list是一个由逗号隔开的符号表,他们可能出现在stuff中,

注意:

参数列表的左括号必须与name紧邻,如果两者之间有任何空⽩存在,参数列表就会被解释为stuff的⼀部分。

例如:

注意,#define只是完成了替换,这时候又一个问题出现了,运算符优先级问题.比如:

为了避免这种问题,我们要对每个x加上 (),这样就解决问题了,可能比较麻烦

#define SQUARE(x) ((x)*(x))

这样写是最安全的,可以避免由于运算符优先级出现的问题

4、带有副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候的时候,如果宏参数带有副作用,那么在使用这个宏的时候就会出现一些不好的后果,副作⽤就是表达式求值的时候出现的永久性效果。

x+1 //没有副作用

x++ //有副作用

比如:

这是不带副作用的情况下,a和b的值都不变

这时候我们要注意,尽量避免这样写

5、宏的替换规则

1.在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号,如果是,他们先替换

2.替换文本被插入到程序中原来的位置中,这些都是在预处理阶段就完成的

3.最后,再次对文件进行扫描,看看是否还有包含#define定义的符号,如果是,就重复上述步骤;

注意:

1.宏参数和宏定义中可以出现其他宏定义的符号,但是不能出现递归;

2.当预处理器搜索#define定义的符号时,字符串常量的内容并不被搜索;

比如:

字符串中的M并不被搜索

6、宏和函数的对比

了解一下7、

7、#和##

#运算符将宏的一个参数转换成字符串字面量,他仅允许出现在带参数的替换列表中

#运算符执行的操作可以理解为"字符串化"

比如:

##运算符可以把位于他两边的符号合成一个符号,##被称为记号粘合,对于这个东西用的少,能看懂被人用的什么意思就好

比如:

看看就好,用的不多

8、命名约定

平常的习惯是宏名全部大写

函数名不要全部大写

9、#undef

这条指令用于移除一个宏定义

#undef NAME

//如果现存的⼀个名字需要被重新定义,那么它的旧名字⾸先要被移除。

比如:

我们要修改M的值,要先移除这个宏,然后再重新定义;

10、条件编译

在编译一个程序的时候我们如果要将一条语句编译或者放弃的时候,可以用条件编译指令

比如:

常见的条件编译指令:

1.单分支的条件编译

#if 常量表达式 //如果表达式语句为真,则执行#if里面的语句,否则不执行

//语句

#endif

2.多分支的条件编译:

#if 常量表达式

//语句

#elif 常量表达式

//语句

#elif 常量表达式

//语句

#else

//语句

#endif

比如:

只有第一个表达式为真,所以只执行第一条语句,如果有多个表达式都为真, 则只执行最先为真的那一个表达式

3.判断是否被定义

#ifdef 宏常量

//语句

#endif

他还·等价于

#if defined(宏常量)

//语句

#ednif

如果宏常量被定义,那么就执行条件里面的语句,否则不执行,比如:

红框框里的两条语句时等价的;

如果__DEBUG__被定义,则执行语句,否则不执行

4.嵌套指令

#if defined(OS_UNIX)

#ifdef OPTION1

unix_version_option1()

#endif

#ifdef OPTION2
unix_version_option2();
#endif

#elif defined(OS_MSDOS) #ifdef OPTION2
msdos_version_option2();

#endif

#endif

11、头文件的包含方式

本地文件包含:

#include"filename.h"

先在源文件所在目录下查找,如果找不到,就在标准库头文件里面查找,如果还找不到,就提示编译错误;

库文件包含:

#include<stdio.h>

查找库函数头文件直接在标准库里面去查找,如果找不到就提示编译错误

如果是这样,那么标准库头文件也可以用" " 来包含?

是这样的,但是这样查找效率低下,而且也不容易区分是库文件还是本地文件

12、嵌套文件包含

我们已经知道#include指令可以使另一个文件被编译,就是在#include这行指令处展开被编译的文件,但是如果我们重复包含,对编译的压力就会比较大,运行起来比较慢

如何解决头文件被重复包含的问题?

条件编译

在每个头文件的开头写:

#ifndef TEST_H

#define TEST_H

//语句

#endif

意思是:

如果 TEST_H 没有被定义,那么 #define TEST_H 定义,然后执行头文件的内容,

如果包含的有相同的头文件,先判断 #ifndef TEST_H 发现 TEST_H 被定义了,那么不再执行头文件中的内容;

另外还有一种更加方便的写法:

#pragma once

作用和上面是一样的,都是防止头文件被重复包含。

相关推荐
浅陌pa2 小时前
24:RTC实时时钟
c语言·stm32·单片机·嵌入式硬件
Reese_Cool2 小时前
【二级C语言考试】自定义数据类型
c语言·开发语言
我命由我123452 小时前
GPIO 理解(基本功能、模拟案例)
linux·运维·服务器·c语言·c++·嵌入式硬件·c#
只对您心动4 小时前
【C高级】有关shell脚本的一些练习
linux·c语言·shell·脚本
小珑也要变强6 小时前
队列基础概念
c语言·开发语言·数据结构·物联网
咩咩大主教10 小时前
C++基于select和epoll的TCP服务器
linux·服务器·c语言·开发语言·c++·tcp/ip·io多路复用
时光飞逝的日子10 小时前
多重指针变量(n重指针变量)实例分析
c语言·指针·多重指针·双重指针·n重指针·指针变量
Ylucius11 小时前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
.普通人12 小时前
c语言--力扣简单题目(回文链表)讲解
c语言·leetcode·链表
星迹日12 小时前
C语言:联合和枚举
c语言·开发语言·经验分享·笔记