当前位置:网站首页>【C语言】预处理详解
【C语言】预处理详解
2022-07-23 01:18:00 【命由己造~】
预编译
一、程序的翻译环境和执行环境
1.1 翻译环境
C语言每次运行完一个程序后会出现可执行程序.exe文件,那么这是怎么从.c文件变成.exe文件呢?
1️⃣ 所有源文件(头文件会拷贝到源文件中)通过编译器转换成目标文件(.obj)
2️⃣ 目标文件由链接器结合在一起,形成一个可执行程序

而这里编译器处理的编译过程也可以分为三个部分:
1.2 预处理->编译->汇编->链接
预处理:
1️⃣ 包含头文件
2️⃣ 宏替换
3️⃣ 去注释
test.c -> test.i
编译:
1️⃣ 把C语言代码转化为汇编代码
2️⃣ 进行了 语法分析,词法分析,语义分析,符号汇总(全局符号例如main函数、函数名)
test.i -> test.s
汇编:
1️⃣把汇编代码转换成二进制指令
2️⃣ 形成符号表(函数名加上地址)
test.s -> test.o
链接:
1️⃣ 合并段表(把相同的内容合并到一个区域)
2️⃣ 符号表的合并和重定位(声明处的地址没有意义,合并选择有意义的)
test.o -> text.exe
1.3 运行环境
程序执行的过程:
1)程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
2)程序的执行便开始。接着便调用main函数。
3)开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
4)终止程序。正常终止main函数;也有可能是意外终止。
二、详解预处理
2.1 预定义符号
C语言提供了一些能直接被使用的符号:
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C,其值为1,否则未定义
int main()
{
FILE* pf = fopen("test.txt", "w");
if (pf == NULL)
{
return EXIT_FAILURE;
}
for (int i = 0; i < 5; i++)
{
fprintf(pf, "file:%s line:%d date:%s time:%s\n", __FILE__, __LINE__, __DATE__, __TIME__);
}
fclose(pf);
pf = NULL;
return 0;
}

2.2 #define
语法:
#define name stuff
#define MAX 100
#define STR "abc"
int main()
{
printf("%d %s", MAX, STR);
return 0;
}
预处理后:
int main()
{
printf("%d %s", 100, "abc");
return 0;
}
#define的后面不要加 ;
续航符:
#define DEBUG_PRINT printf("file:%s\tline:%d\t \ date:%s\ttime:%s\n" ,\ __FILE__,__LINE__ , \ __DATE__,__TIME__ )
2.3.1 #define定义宏
#define name( parament-list ) stuff
比如现在实现一个平方的宏:
#define SQUARE( x ) ((x) * (x))
int main()
{
printf("%d", SQUARE(5));
return 0;
}
因为宏是直接替换代码,所以有些情况下会有符号优先级问题,所以应该尽量多加一些括号。
2.3.2 #define 替换规则
在程序中扩展#define定义符号和宏时,需要涉及几个步骤。
- 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先
被替换。- 替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
- 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上
述处理过程。
注意:
1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
2.3.3 #和##
#:
把参数写进字符串
#define PRINT(n) printf("the value of " #n " is %d\n", n)
int main()
{
int n = 10;
PRINT(n);
return 0;
}

#n会转化成"n"。
##:
合并符号
#define CAT(a, b) a##b
int main()
{
printf("%s\n", CAT("abc", "def"));
int ABC = 1;
printf("%d\n", CAT(A, BC));
return 0;
}

2.3.4 带副作用的宏
#define MAX(a, b) (a) > (b) ? (a) : (b)
int main()
{
int a = 5;
int b = 4;
int m = MAX(a++, b++);
printf("%d\n", m);
printf("%d %d", a, b);
return 0;
}
结果:
6
7 5
本意是求较大值,这种重复计算就是副作用。
2.3.5 宏和函数
比较大小的宏和函数:
#define MAX(a, b) a > b ? a : b
int Max(int a, int b)
{
return (a > b ? a : b);
}
宏的优点:
1️⃣ 函数必须声明类型,而宏不用,宏是类型无关的。
2️⃣ 宏的效率要高于函数,因为函数需要创建栈帧和参数传参等。
宏的缺点:
1)每次使用宏的时候,一份宏定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度。
2) 宏是没法调试的。
3) 宏由于类型无关,也就不够严谨。
4) 宏可能会带来运算符优先级的问题,导致程容易出现错。
2.3 #undef
这条指令用于移除一个宏定义。
#define MAX 100
int main()
{
#undef MAX
MAX;//error
return 0;
}
2.4 条件编译
在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件编译指令。
#define __DEBUG__
int main()
{
int i = 0;
int arr[10] = {
0 };
for (i = 0; i < 10; i++)
{
arr[i] = i;
#ifdef __DEBUG__
printf("%d\n", arr[i]);//为了观察数组是否赋值成功。
#endif //__DEBUG__
}
return 0;
}
如果满足条件就让printf()参与编译,不满足就不参与编译。
int main()
{
#if 0
printf("abc");
#endif
return 0;
}
多个分支的条件编译:
#define M 3
int main()
{
#if M < 5
printf("<");
#elif M == 5
printf("==");
#else
printf(">");
#endif
return 0;
}
判断是否被定义:
#define A 1
int main()
{
#if defined (A)
//#ifdef A
//#if !defined(A)
//#ifndef A
printf("YES");
#endif
return 0;
}
2.5 文件包含
为了防止文件被重复包含:
#ifndef __TEST_H__
#define __TEST_H__
.....
#endif
边栏推荐
- [cann training camp] learning notes - Comparison between diffusion and Gan, dalle2 and Party
- The pit trodden by real people tells you to avoid the 10 mistakes often made in automated testing
- 2022.7.22-----leetcode.757
- 2022.7.22-----leetcode.757
- 十. 实战——云服务器
- transformer汇总
- Advantages of implementing automatic network performance monitoring
- Advantages of server hosting, server leasing and virtual machine
- 2000. reverse word prefix
- 为什么使用Well-Architected Framework?
猜你喜欢

Mathematical modeling -- graph and network models and methods (II)

VS Code快捷键设置

Airserver third party projection software v7.3.0 Chinese Version (airplay terminal utility)

RNA 25. SCI文章中只有生信没有实验该怎么办?

万物互联时代,看IoT测试如何应对“芯”挑战

PyG利用MessagePassing搭建GCN实现节点分类

如何高效系统学习 MySQL?

推荐系统专题 | 推荐系统架构与单域跨域召回模型

软件测试面试思路技巧和方法分享,学到就是赚到

Wallys/DR4019S/IPQ4019/11ABGN/802.11AC/high power
随机推荐
In the era of Internet of everything, see how IOT test meets the challenge of "core"
PyTorch可视化
一文带你了解如何用SQL处理周报数据
[try to hack] awvs installation and simple use
Learn the distributed architecture notes sorted out by Alibaba in one month
Online matting and background changing and erasing tools
Summary of some open source libraries that drive MCU hardware debugger (including stlink debugger)
我是新手,听说开户有保本的理财产品,是什么?
RNA 25. What should we do if there is only Shengxin but no experiment in SCI articles?
Wallys/PD-60 802.3AT Input Output802.3AT/AT 85% Efficiency 10/100/1000M GE Surge Protection
Huawei applications have called the checkappupdate interface. Why is there no prompt for version update in the application
软件测试面试思路技巧和方法分享,学到就是赚到
C#之winform窗体的最大化、最小化、还原、关闭以及窗体的移动
Props and context in setup
作物叶片病害识别系统
教育机器人对学生学习效果的实际影响
Understand the box model, and the basic methods of box model's frame, internal and external margins, horizontal layout, vertical layout, setting floating, and dealing with height collapse
Avantages de la salle des machines bgp
Stream操作之 先分组再取最大值
Anti attack based on conjugate gradient method