11预处理命令下

11预处理命令下

程序的编译,是一个复杂的过程,其中重要的是三个阶段:预处理阶段,编译阶段和链接阶段

1. 初识宏定义

一个宏定义只能占一行代码,这可不是你所认为的一行代码,而是编译器所认为的一行代码

正如上图所示,宏定义以 #define 作为语句的开头,之后两部分,用空格分隔,在预处理阶段期间,会把代码中的 A 内容替换成 B 内容,以此来最终生成“待编译源码”。

宏定义关键字、原内容和替换内容 三者必须写到一行。

可以看到,我们定义了一个支持两个参数的宏,名字为 mul,替换的内容为 a * b。注意,替换内容中的 a 是宏参数中的 a,b 也是宏参数中的 b。这里我再强调一下,理解宏的工作过程,始终离不开那句话:宏做的就是简单替换。

#include <stdio.h>
#define mul(a, b) a * b

int main() {
    printf("mul(3, 5) = %d\n", mul(3, 5));
    printf("mul(3 + 4, 5) = %d\n", mul(3 + 4, 5));
    return 0;
}

“待编译源码”决定了最终程序的功能。
宏做的就是简单的替换。

宏在预处理阶段将被展开,变成“待编译源码”中的内容,并且做的仅仅是简单的替换。也就是说,mul(a, b) 这个宏,替换的形式是 a * b;而 mul(3 + 4, 5) 中 3 + 4 是参数 a 的内容,5 是 b 的内容,依次替换为 a*b 式中的 a,b 的话,最终得到的替换内容应该是 “3 + 4 * 5”,这个才是“待编译源码”中真正的内容。面对这个替换以后的表达式,你就知道为什么输出的结果是 23,而不是 35 了吧。

mul 的使用形式虽然和函数类似,可实际运行原理和函数完全不一样

C 语言给我们提供了一种在行尾加 \(反斜杠)的语法,以此来告诉编译器,本行和下一行其实是同一行内容。这样就做到了:人在阅读代码的时候,看到的是两行代码,而编译器在解析的时候,会认为是一行代码,也就解决了复杂的宏定义的可读性的问题。

#include <stdio.h>
#define swap(a, b) { \
    __typeof(a) __temp = a; \
    a = b, b = __temp; \
}

int main() {
    int num_a = 123, num_b = 456;
    swap(num_a, num_b);
    printf("num_a = %d\n", num_a);
    printf("num_b = %d\n", num_b);
    return 0;
}

需要特别注意的是,代码中反斜杠的后面,不能出现任何其他内容。

3. 初识条件编译

条件编译,就是预处理阶段的条件分支语句,其主要作用是根据条件,决定“源代码”中的哪些代码,接下来会被预处理继续进行处理。

思考题:没有 Bug 的 MAX 宏

请你完善下面代码中的 MAX 宏,MAX 宏的作用,就是接受两个元素,选择出两个元素中的最大值。完善以后的 MAX 宏,输出需要与如下给出的输出样例一致,注意,只能修改 MAX 宏的定义内容,不可以修改主函数中的内容。

#define log(frm, args...) 

如上代码所示,在最后一个参数后面,加上三个点,就代表,这个宏除了第一个 frm 参数以外,后面接收的参数个数是可变的,那么后面的参数内容,统一存放在参数 args 中。

#define log(frm, args...) printf(frm, args)

编译器会预设一些宏,这些宏会为我们提供很多与代码相关的有用信息

可移植性==你写了一份代码,当你的运行环境发生改变时,你的代码到底要不要做修改?如果要做修改,到底要做多少修改?

总结

  1. 宏定义只占用一行代码,为了增强宏定义的代码可读性,我们可以采用在行尾加反斜杠的技巧,来使得上下两行代码,变成编译器眼中的一行代码。
  2. 宏的作用,就是替换,要想理解最终的代码行为,必须从宏替换以后的代码入手分析。
  3. 条件编译相当于一种预处理阶段的代码剪裁技巧。
  4. 编译器预设的宏,有标准的,也有非标准的,非标准的代码会影响其可移植性。

猜你喜欢

转载自www.cnblogs.com/liugangjiayou/p/12609521.html