预处理机制

跟着肯哥(不是我)学预处理机制

预处理类别

宏定义:#define

  • 将文本替换为表达式或语句

条件编译:#ifdef#ifndef#if#elif#endif

  • 根据标识符是否被定义选择编译代码

头文件包含:#include

  • 将其他文件(通常是头文件)包含到当前文件中,以便在当前文件中使用头文件中定义的内容

预处理流程

  1. 预处理指令识别
  2. 宏替换
  3. 条件编译
  4. 头文件包含
  5. 行连接处理
  6. 宏展开和条件编译的递归处理

预处理指令识别

扫描源代码并识别以#开头的预处理指令。

宏替换

预处理器根据宏定义将代码中的宏名称替换为指定的文本。
可以是常量替换,也可以是带参数的宏函数替换
预处理器会根据宏定义展开宏

条件编译

根据条件选择性地编译不同的代码块。
#ifdef#ifndef#if#elif#endif

头文件包含

通过#include指令,将其他文件(通常是头文件)包含到当前文件中,以便在当前文件中使用头文件中定义的内容

预处理的行连接处理

在C语言中,行连接处理是预处理阶段的一部分,用于将一行代码书写为多行,以保持代码可读性。
行连接操作通过反斜杠\字符实现。一行代码以反斜杠字符结尾时,该行代码将与下一行自动连接为一行。
预处理机制_第1张图片
换行之后的进位也会被当作空格输出出来。
行连接在预处理阶段,在编译阶段看到代码时,已经是连接在一起的一行。这种处理不会影响编译器的语义分析和中间代码。

宏展开和条件编译的递归处理

在进行宏展开和条件编译时,如果遇到新的预处理指令,预处理器会递归地处理这些指令。肯呢个会触发更多的宏展开和条件编译。

删除注释

预处理器将源代码中的注释删除,注释在预处理阶段无需保留,不会影响编译器的输出。

预编译的警告信息和错误信息

可以使用#error#warning预处理指令来生成编译错误和警告信息。这些指令在预处理时发出特定的错误或警告信息,让开发者知道代码中存在的问题或需要注意的地方
预处理机制_第2张图片
#error#warning指令生成的信息只会在预处理阶段发出,而不会影响到最终的目标代码,它们在编译阶段不会生成任何错误或警告信息。
预处理机制_第3张图片

查看预处理后的源文件

以肯哥用的GCC为例。
可以使用-E选项来查看预处理后的源文件。只进行预处理操作,不进行编译、汇编和链接。将预处理后的源文件输出到标准输出流,一般是控制台。
gcc -E example.c
编译器会将预处理后的内容输出到控制台。
如果你想将预处理后的源文件保存到一个文件中,可以使用重定向操作符>将输出重定向到一个文件中。
gcc -E example.c > preprocessed.c
上述命令会将预处理后的源文件保存到名为preprocessed.c的文件中。

重点强调

  • 宏定义的命名规范
  • 宏定义与函数调用的冲突
  • #include不单单是只能包含头文件:include的本质就是纯文本包含。
  • 善用条件编译:在庞大工程代码的配置裁剪工作中,绝大多数的技术手段就是利用条件编译,对不同的代码选用和删除,达到代码裁剪的效果。

宏定义

宏定义用于在编译之前将代码中的标识符替换为指定的文本。
宏定义可以分为带参数的宏定义和不带参数的宏定义。

#define PI 3.14
#define square(x) ((x)*(x))

条件编译

#ifdef DEBUG_MODE
printf("debug mode");
#endif

内置的宏定义

有一部分宏定义是编译器提供的,它们可以用于获取一些编译器和操作系统的信息。

  • __FILE__:该宏展开为当前源文件的文件名字符串。
  • __LINE__:该宏展开为所在代码行的行号
  • __DATE__:展开为一个字符串常量,表示源文件的编译日期
  • __TIME__:展开为一个字符串常量,表示源文件的编译时间

预处理机制_第4张图片
这些宏定义的值在每次编译时都会被重新计算,它们的值可能会因编译时间的不同而不同。

宏定义操作符

###是两个特殊的运算符,用于处理宏的参数和文本替换。

##用于将两个标识符连接在一起形成新的标识符

预处理机制_第5张图片

#用于将宏的参数替换为字符串

预处理机制_第6张图片

注意事项

  • ##只能用于连接标识符。需要保证连接后的标识符合法
  • #运算符只能用于将宏的参数转换为字符串常量,不能用于其他类型的表达式
  • 在使用###时,要保证加适当的空以避免出现错误或合并的情况

预处理机制_第7张图片

宏定义命名原则、

  • 使用大写字母命名宏,以与变量和函数区分开
  • 使用括号确保宏参数和运算的正确性
  • 避免定义复杂的宏
  • 考虑宏定义潜在的副作用

你可能感兴趣的:(算法,linux,运维)