政安晨的个人主页:政安晨
欢迎 👍点赞✍评论⭐收藏
收录专栏: 机器学习智能硬件开发全解
希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正!
C语言程序的预处理过程是在编译阶段之前进行的,主要包括以下几个步骤:
-
去除注释:预处理器会将程序中的注释去除,不会传递给编译器。
-
头文件包含:预处理器会根据程序中的#include指令,将对应的头文件内容插入到程序中。这样可以方便地使用已经定义好的变量、函数等。
-
宏替换:预处理器会根据程序中定义的宏,将宏的引用替换为相应的内容。例如,将宏定义的常数替换为实际的数值,将宏定义的函数调用替换为函数体。
-
条件编译:预处理器会根据程序中的条件编译指令,决定是否编译相应的代码段。例如,根据条件判断是否编译某个平台的特定代码。
-
处理特殊指令:预处理器还可以处理特殊的预处理指令,例如#define、#ifdef等,对程序代码进行进一步的处理。
预处理器处理完程序后,生成的预处理文件会被传递给编译器进行编译。
预处理过程
为了方便编程,编译器一般为开发人员提供一些预处理命令,使用#标识。
我们常见的预处理命令如下:
● 头文件包含:#include。
● 定义一个宏:#define。
● 条件编译:#if、#else、#endif。
● 编译控制:#pragma。
编译器提供的这些预处理命令,大大方便了程序的编写:
通过头文件包含可以实现模块化编程;
使用宏可以定义一个常量,提高程序的可读性;
通过条件编译可以让代码兼容不同的处理器架构和平台,以最大限度地复用公用代码。
通过#pragma预处理命令可以设定编译器的状态,指示编译器完成一些特定的动作。
● #pragma pack([n]):指示结构体和联合成员的对齐方式。
● #pragma message("string"):在编译信息输出窗口打印自己的文本信息。
● #pragma warning:有选择地改变编译器的警告信息行为。
● #pragma once:在头文件中添加这条指令,可以防止头文件多次编译。
预处理过程,其实就是在编译源程序之前,先处理源文件中的各种预处理命令。
编译器是不认识预处理指令的,在编译之前不先把这些预处理命令处理掉,编译器就会报错。
预处理主要包括以下操作:
● 头文件展开:将#include包含的头文件内容展开到当前位置。
● 宏展开:展开所有的宏定义,并删除#define。
● 条件编译:根据宏定义条件,选择要参与编译的分支代码,其余的分支丢弃。
● 删除注释。
● 添加行号和文件名标识:编译过程中根据需要可以显示这些信息。
● 保留#pragma命令:该命令会在程序编译时指示编译器执行一些特定行为。
一个源程序在预处理前后有什么变化呢?
重写前面的C程序
我们写了一个测试程序,分别使用预处理命令去定义一些宏和条件编译。
(咱们在上一篇文章中代码的基础上修改一下)上一篇文章:
【机器学习智能硬件开发全解】(八)—— 政安晨:通过ARM-Linux掌握基本技能【C语言程序的编译/链接/安装运行】https://blog.csdn.net/snowdenkeke/article/details/136805174对上一章中的main.c文件重写如下:
#include <stdio.h>
#include "sub.h"
#define PI 3.14
int global_val = 1;
int uninit_val;
void platform_init()
{
#ifdef ARM
printf("ARM platform init ... \n");
#else
printf("X86 platform init ... \n");
#endif
return;
}
#pragma pack(2)
#pragma message("build main.c ...\n")
float f = PI;
int main(void)
{
platform_init();
int a, b;
static int local_val = 2;
static int uninit_local_val;
a = add(2, 3);
b = sub(5, 4);
printf("a = %d\n", a);
printf("b = %d\n", b);
return 0;
}
对上面的C程序只作预处理操作,不编译,将输出的信息重定向到main.i文件。
## 在命令行控制台执行下面命令,并将预编译的信息写入main.i文件中
arm-linux-gnueabihf-gcc -E main.c > main.i
## 查看预编译文件
cat main.i
main.i中的部分信息如下:
# 0 "main.c"
# 0 "<built-in>"
# 0 "<command-line>"
# 1 "/usr/arm-linux-gnueabihf/include/stdc-predef.h" 1 3
# 0 "<command-line>" 2
# 1 "main.c"
# 1 "/usr/arm-linux-gnueabihf/include/stdio.h" 1 3
# 27 "/usr/arm-linux-gnueabihf/include/stdio.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/bits/libc-header-start.h" 1 3
# 33 "/usr/arm-linux-gnueabihf/include/bits/libc-header-start.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/features.h" 1 3
# 392 "/usr/arm-linux-gnueabihf/include/features.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/features-time64.h" 1 3
# 20 "/usr/arm-linux-gnueabihf/include/features-time64.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/bits/wordsize.h" 1 3
# 21 "/usr/arm-linux-gnueabihf/include/features-time64.h" 2 3
# 1 "/usr/arm-linux-gnueabihf/include/bits/timesize.h" 1 3
# 22 "/usr/arm-linux-gnueabihf/include/features-time64.h" 2 3
# 393 "/usr/arm-linux-gnueabihf/include/features.h" 2 3
# 489 "/usr/arm-linux-gnueabihf/include/features.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/sys/cdefs.h" 1 3
# 559 "/usr/arm-linux-gnueabihf/include/sys/cdefs.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/bits/wordsize.h" 1 3
# 560 "/usr/arm-linux-gnueabihf/include/sys/cdefs.h" 2 3
# 1 "/usr/arm-linux-gnueabihf/include/bits/long-double.h" 1 3
# 561 "/usr/arm-linux-gnueabihf/include/sys/cdefs.h" 2 3
# 490 "/usr/arm-linux-gnueabihf/include/features.h" 2 3
# 513 "/usr/arm-linux-gnueabihf/include/features.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/gnu/stubs.h" 1 3
# 10 "/usr/arm-linux-gnueabihf/include/gnu/stubs.h" 3
# 1 "/usr/arm-linux-gnueabihf/include/gnu/stubs-hard.h" 1 3
# 11 "/usr/arm-linux-gnueabihf/include/gnu/stubs.h" 2 3
# 514 "/usr/arm-linux-gnueabihf/include/features.h" 2 3
# 34 "/usr/arm-linux-gnueabihf/include/bits/libc-header-start.h" 2 3
# 28 "/usr/arm-linux-gnueabihf/include/stdio.h" 2 3
......
......
......
# 2 "main.c" 2
# 1 "sub.h" 1
# 1 "sub.h"
int add(int nCountA, int nCountB);
int sub(int nCountA, int nCountB);
# 3 "main.c" 2
int global_val = 1;
int uninit_val;
void platform_init()
{
printf("X86 platform init ... \n");
return;
}
#pragma pack(2)
# 21 "main.c"
#pragma message("build main.c ...\n");
float f = 3.14;
int main(void)
{
int a, b;
static int local_val = 2;
static int uninit_local_val;
a = add(2, 3);
b = sub(5, 4);
printf("a = %d\n", a);
printf("b = %d\n", b);
return 0;
}
通过预处理前后源文件的变化对比,我们可以看到:
当预处理器遇到#include命令时,会直接将包含的头文件内容展开,并删除#include;
当遇到#define宏时,执行同样的操作。
当遇到条件编译指令时,会根据开发者定义的宏标记,选择要参与编译的代码部分,其余部分删除,经过预处理后,#pragma保留,指示编译器在后续的编译阶段执行一些特定的操作。
继续编译预处理后的C程序,在编译信息提示窗口里,我们会看到自己添加的编译提示信息。