C语言编译连接过程和宏
来源:互联网 发布:为什么知乎很多反政府 编辑:程序博客网 时间:2024/06/16 07:56
C语言代码到二进制代码中间的四个转换:
1.预处理(宏替换、取注释、头文件展开、条件编译)
对应的linux指令(gcc -E test.c -o test.i)//E预处理指令,在预处理之后停止,生成test.i文件
2.编译 (C语言代码 转换为 汇编代码)
(gcc -S test.i -o test.s)//S编译指令,在编译之后停止,生成test.s文件
3.汇编 (汇编代码 转换为 二进制代码)
(gcc -c test.s -o test.o)//c汇编指令,在汇编之后停止,生成test.o文件
4.链接 (将不同部分的代码和数据搜集组合成一个单一文件的过程)
(gcc test.o -o mytest)//链接指令,在链接之后停止,生成可执行文)
宏
1.什么是宏?
#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或者宏定义
ANSI 标准定义了如下几个宏
_LINE_ 表示正在编译的文件行号
_FELE_ 表示正在编译的文件名字
_DATE_ 表示正在编译时刻的日期字符串
_TIME_ 表示编译时刻的时间字符串
_STDC_ 表示该文件是不是定义成标准的C程序,其值为1,否则未定义
2.#define替换
在程序展开#define定义和宏的几个步骤
1.调用宏时,检查参数本身是不是一个宏,如果是#define定义的符号,替换它
2.替换文本插入到原来位置,对于宏,参数名被它们的值替换
3.最后在进行扫描,看是否还有#define定义的符号,如果有替换它
例:#include<stdio.h>
#define M 10
#define SUM(x) ((x)*(x)+(M))
int main()
{
int a = 0;
a = SUM(M);
printf("a = %d\n",a);
return 0;
}
在预处理时候a=SUM(M)先被替换成a=SUM(10)
a=SUM(10)在被替换成a=((10)*(10)+(M))
最后进行扫描发现还有被#define定义 的符号M,则在一次进行替换 a=((10)*(10)+(10))
在linux下生成进行完成预处理的文本
![](file:///C:/Users/lenovo/AppData/Local/Temp/enhtmlclip/Image.png)
3.在定义宏函数时的注意事项
1.每个实参实例应该被小括号括起来
例:
a.#define SQRT(x) x*x
当你SQRT(4+1)这样调用时,它会被替换成4+1*4+1==9,显然这个结果是不符合预期的
所以你应该这样定义#define SQRT(x) (x)*(x),当然这样也是不完美的!
b.#define SUM(x) (x)+(x)
当你2*SUM(5)这样调用时,它会被替换成2*(5)+(5)==15,这样得到一个不符合预期的结果
所以应该更加小心的定义它#define SUM(x) ((x)+(x))和#define SQRT(x) ((x)*(x))
2.用结构封装语句序列并确保其正确
例:
#include<stdio.h>
#define FUN() fun1();fun2()
#define fun1() printf("hello,")
#define fun2() printf("world\n")
int main() int main()
{ {
int flag = 1; int flag = 1;
if(flag) if(!flag)
FUN(); FUN();
return 0; return 0;
} }
结果:hello,world world
为什么有差异呢?看看预处理之后的结果
可以清楚地看出,与处理之后的结果与我们预想的逻辑并不是很相符
因为在定义宏函数时不能判断用户的使用环境,所以我们采用do-while-zero结构进行封装
我们把第一句宏定义经行处理 #define FUN() do{fun1();fun2();}while(0)
在预处理之后我们可以看到这样的结果
![](file:///C:/Users/lenovo/AppData/Local/Temp/enhtmlclip/Image(1).png)
这个结果就符合我们的逻辑预期。
4. 宏参数插入字符串常量的方法
1.临近字符串的连接特性
#define M 9
#define PRINT (FORMAT,VALUE)\
printf("The value is "FORMAT"\n",VALUE)
PRINT("%d",M);
结果:The value is 9
2.使用"#"运算符进行处理
#define M 9
#define PRINT(FORMAT,VALUE) \
printf("The value of "#VALUE" is "FORMAT"\n",VALUE)
PRINT("%d",M);
结果:The value of M is 9
"#"用于函数宏的替换部分,可以将一个宏参数转换成一个字符串
3."##"它把它两边的符号连接成一个符号
#include<stdio.h>
#define M 9
#define N 10
#define MN 20
#define PRINT() printf("%d\n", M##N)
int main()
{
PRINT();
int flag = 1;
return 0;
}
结果 : 20
在预处理时M##N连接在一块成为MN,MN又是一个被定义的宏,值为20
5.宏与函数的比较
#define宏
函数
代码长度
每次使用时,进行代码替换。代码出现在程序中,导致程序长度大幅增长
每次使用时
执行速度
快
存在函数调用和返回时的额外开销
参数求值
参数每次用于宏定义,他们都将重新求值。
参数只在调用前求值一次
参数类型
与类型无关
与类型有关
操作符优先级
对于宏函数中的参数求值应该非常小心,除非加上括号,否则会因为优先级的原因产生不可预料的结果
函数参数只是在调用的时候求值一次,它的结果传递给函数,表达式的结果更容易预料
1 0
- C语言编译连接过程和宏
- C语言编译过程
- C语言编译过程
- C语言编译过程
- C语言编译过程
- c语言编译过程
- C语言编译过程
- C语言编译过程
- C语言编译过程
- C语言编译过程
- c语言编译过程
- C语言编译过程
- c语言编译过程
- C语言编译过程
- c语言编译过程
- c语言编译过程
- C语言编译过程
- C语言编译过程
- Javascript判断Crontab表达式是否合法
- [SetPropertiesRule]{Server/Service/Engine/Host/Context} Setting property 'source' to 'org.eclipse.j
- 有关线程的函数
- 图片无法加载时onerror处理
- Javascript模块化编程(一)
- C语言编译连接过程和宏
- 纯css制作带三角的边框(带效果图)
- NOIP 2014 螺旋矩阵
- 函数中.call/.bind/.apply三者的区别
- java-mysql基本操作笔记2
- api-title
- new-title
- Vue.js中v-show和v-if使用时的注意事项
- 模型训练之参数选择