预处理笔记

目录

1. 预定义符号

2. #define

2.1 定义标识符

2.2 定义宏

2.3 替换规则

2.4 #和##

2.5 带副作用的宏参数



1. 预定义符号

__FILE__            //进行编译的源文件
__LINE__           //文件当前的行号
__DATE__          //文件被编译的日期
__TIME__           //文件被编译的时间
__STDC__          //如果编译器遵循ANSI C(2013前的版本),其值为1,否则未定义

这些都是预处理都是语言内置的,可以直接使用

2. #define

2.1 定义标识符

就像这样,定义了一个符号,一般用大写字母:

#define MAX 100                //定义了一个符号MAX 

#define reg register            //给register取了一个简短的名字reg

#define CASE break;case     //在写case语句的时候自动把 break写上。


// 如果定义的 stuff过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf("file:%s\tline:%d\t \
             date:%s\ttime:%s\n" ,\
             __FILE__,__LINE__ ,    \
             __DATE__,__TIME__ ) 

 需要注意的是,不要在宏定义后面加分号!

#define MAX 100;

int main()

{

        printf("%d\n",MAX);                //替换完成后: printf("%d\n",100;);  导致语法错误

}

2.2 定义宏

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

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

#define SQUARE(x)  x*x

宏接受到一个参数x,就会替换为x*x

 SQUARE(5)  5*5

但是这个宏是存在问题的!

 SQUARE(5+1)  5+1*5+1                //因为涉及运算符优先级的原因,结果并不会是我们预想的36,而是11

so

 #define SQUARE(x) ((x)*(x))            //设计宏定义时要充分考虑这个问题

替换结果

 SQUARE(5+1) ((5+1)*(5+1))        //36

2.3 替换规则

在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先
被替换。
2. 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上
述处理过程。
注意:
1. 宏参数和#define 定义中可以出现其他#define定义的变量。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。

2.4 #和##

那么该怎么把参数插入到字符串中呢?

char* p = "hello ""bit\n";
printf("hello"," bit\n");
printf("%s", p);

输出:

hello bit 

我们发现字符串有自动拼接的特点,那么就可以这样

int i = 10;
#define PRINT(FORMAT, VALUE)\
printf("the value of " #VALUE "is "FORMAT "\n", VALUE);
...
PRINT("%d", i+3);

 代码中的#VALUE会被预处理为

"VALUE"

so,最后的输出结果为

the value of i+3 is 13

 ##的作用

##可以把位于它两边的符号合成一个符号。
它允许宏定义从分离的文本片段创建标识符。

#define ADD_TO_SUM(num, value) \
sum##num += value;
...
ADD_TO_SUM(5, 10);//作用是:给sum5增加10.

2.5 带副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能
出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。
例如:

x++;  //不带副作用

x+1;  //带有副作用

 #define MAX(a,b)((a)>(b)?(a):(b))

...

x=5;

y=7;

z=MAX(x++,y++);

printf("%d\n",z);                //这里输出什么呢?

 预处理后的结果大概是这样的

z = ( (x++) > (y++) ? (x++) : (y++));

 结果:

x,y的值都被修改了,且值不可控,不建议这样使用 

2.6 函数和宏的对比

#define定义宏函数
代码长度每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码
执行速度更快存在函数调用和返回的额外开销,相对较慢一些
操作符优先级宏参数的求值是在所有周围表达式的上下文环境里,除非加上括号,否则邻近操作符的优先级可能会产生不可预料的后果,所以建议宏在书写的时候多些括号。函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。
带有副作用的参数参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会产生不可预料的结果。函数参数只在传参的时候求值一次,结果更容易控制。
参数类型宏的参数与类型无关,只要对参数的操作是合法的,它就可以使用于任何参数类型。函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是不同的。
调试宏不方便调试函数可以逐语句调试
递归不能递归可以递归

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

danae_c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值