【C语言底层】预处理 超详解

本文详细介绍了C语言预处理指令,包括预定义符号、#define定义常量和宏、带有副作用的宏参数、宏替换规则、宏与函数的对比、#和##运算符的用法、命名约定、#undef指令、命令行定义、条件编译以及头文件的包含策略,旨在帮助读者深入理解C语言预处理的本质和应用。
摘要由CSDN通过智能技术生成

目录

         前言:

1. 预定义符号

2. #define定义常量

3. #define定义宏

4. 带有副作用的宏参数

5. 宏替换的规则

6. 宏函数的对比

7. #和##

7.1 #运算符

7.2 ##运算符

8. 命名约定

9. #undef

10. 命令行定义

11. 条件编译

12. 头文件的包含

12.1 头文件被包含的方式:

12.1.1 本地文件包含

12.1.2 库文件包含

12.2 嵌套文件包含

13. 其他预处理指令



前言:
预处理指令 都是在 程序 的预处理阶段 执行完毕,在 .i 后缀预处理文件 中 可以发现预处理指令的处理结果

正文开始

1. 预定义符号

C语言设置了一些预定义符号,可以直接使用,预定义符号也是在预处理期间处理的。

__FILE__ //进行编译的源文件  
__LINE__ //文件当前的行号  
__DATE__ //文件被编译的日期  
__TIME__ //文件被编译的时间  
__STDC__ //如果编译器遵循ANSI C,其值为 1 ,否则未定义:如在VS编译器中使用 会报错:说明 VS不遵循ANSI C标准

举个例子:

printf("file:%s line:%d\n", __FILE__, __LINE__);

2. #define定义常量

基本语法:
#define name stuff
举个例子:
1、 定义 数值
 #define MAX 1000
2、为关键字重命名:为 register这个关键字,创建一个简短的名字
 #define reg register
3、 代替 一段代码直接 : define定义一个 符号 代替 一段代码
 #define DEBUG_PRINT printf(" file:%s\n line:%d\n date:%s\n time:%s\n" ,__FILE__,__LINE__ , __DATE__,__TIME__ )
4、(代码若过长可以使用 续行符: 一个 斜杠 + 换行本质是将 换行符“\n”变成 “\\n” 使换行符失效,不再换行,则代码就判定为同一行)
 #define DEBUG_PRINT printf(" file:%s\n \
        line:%d\n date:%s\n time:%s\n" ,\
                    __FILE__,__LINE__ , \
                    __DATE__,__TIME__ )

思考:在定义定义标识符的时候,要不要在最后加上 分号: ? 比如:

#define MAX 1000;
#define MAX 1000

建议不要加上 ; ,这样容易导致问题。 比如下面的场景:

if (condition)
   max = MAX;     (替换后 max = 1000;;   一个分逗号 算做一个语句)
 else
   max = 0;

       如果是加了分号的情况,等替换后,if 和 else 之间就是 2 条语句,而没有大括号的时候,if后边只能有一条语句。这里会出现语法错误。

3. #define定义宏

#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。 下面是宏的申明方式:

#define name( parament-list ) stuff

       其中的 parament - list 是一个由逗号隔开的符号表,它们可能出现在stuff中。 注意: 参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

举例:

1 #define SQUARE( x ) x * x

这个宏接收一个参数x.如果在上述声明之后,你把SQUARE( 5 );

置于程序中,预处理器就会用 下面这个表达式替换上面的表达式:5 * 5

 #include<stdio.h>
 #define SQUARE(x) x*x
 int main()
 {
     int a = 5;
     printf("%d\n", Square(a)); // 在预处理后的 程序中 这一句话变成 printf("%d\n", a * a);
     return 0; 
 }

警告 这个宏存在一个问题: 观察下面的代码段:

int a = 5;
printf("%d\n" ,SQUARE( a + 1 ) );

乍一看,你可能觉得这段代码将打印 36 ,事实上它将打印 11 ,为什么呢? 替换文本时,参数 x 被替换成 a+1, 所以这条语句实际上变成了

printf ( "%d\n",a + 1 * a + 1 );
本质:宏的参数是 直接进行替换的,不会自己运算,当参数中有操作符,宏不会直接遵循一些运算符的优先级,则可能导致不会按照预想的次序求值,
改进:在宏定义上加上两个括号,这个问题便轻松的解决了:
#define SQUARE(x) (x) * (x)
这样预处理之后就产生了预期的效果:
printf ( "%d\n",(a + 1) * (a + 1) );
这里还有一个宏定义:
#define DOUBLE(x) (x) + (x)
定义中我们使用了括号,想避免之前的问题,但是这个宏可能会出现新的错误。
int a = 5;
printf(" %d\n" , 10 * DOUBLE(a));

这将打印什么值呢?看上去,好像打印 100 ,但事实上打印的是55. 我们发现替换之后:

printf ( "%d\n",10 * (5) + (5));
乘法运算先于宏定义的加法,所以出现了 55 .(因为本质 就是直接 替换,直观上的那种的套壳)
这个问题,的解决办法是在宏定义表达式两边加上一对括号就可以了。
#define DOUBLE(x) (( x ) + ( x ))
提示:
所以用于 对数值表达式进行求值的宏定义 都应该用这种方式加上括号,避免在使用宏时由于参数中的
操作符或邻近操作符之间不可预料的相互作用 , 千万别 吝啬使用括号。

4. 带有副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可
  • 34
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值