理解程序编译预处理与链接过程
2016-11-03 11:23
423 查看
首先熟悉一些预处理标识符
__FILE__ :进行编译的文件
__LINE__ :文件当前行号
__DATE__ :文件被编辑的周期
__TIME__ :文件被编辑的时间
# :使用预处理器将宏参数转换成一个字符串
##:将位于它两边的符号连接成一个符号。
示例 :
__FILE__ :用LINUX指令来观察它的预处理结果和最后输出的结果
#include<stdio.h>
int main()
{
printf("file:%s\t,line:%d,date:%s,time:%d\n",__FILE__,__LINE__,__DATE__,__TIME__);
return 0;
}
__LINE__ ,__DATE__ ,__TIME__也是同理,因此将它们一次写完观察预处理之后的结果即gcc -E mytest.c -o mytest.i,也可以直接观察程序运行完之后的结果即gcc mytest.c 然后用./a.out观察程序运行完的结果
由以上结果可以看出程序在预处理之后就已经将__LINE__ ,__DATE__ ,__TIME__这些预处理标识符用 ”mytest.c",4,"Nov 3 2016 ","10:24:40"替换了即预处理之后的结果就等同于程序运行完之后的结果。
示例:
'#‘使用预处理器将宏参数转换成一个字符串代码如下:
#define M 10
#define PRINT(FORMAT,VALUE) printf("the value of" #VALUE"is" FORMAT"\n",VALUE);
#include<stdio.h>
int main()
{
PRINT("%d",M);
return 0;
}
预处理之后的结果:
由以上的结果就可以看出在预处理之后宏参数通过’#‘就已经转换成一个字符串,而程序运行完之后的结果也是宏参数M变成了一个字符串。
’##‘使用是将位于它两边的符号连接成一个符号
2、了解宏和函数
1、宏可以非常频繁地用于执行简单的运算,可以进行参数的替换
2、宏可以进行临近字符串的拼接并且可以替换掉其中的一部分
3、对函数中的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型转换;而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时带入指定的字符即可。宏定义时,字符串可以是任何类型的数据。
4、宏不可以出现递归
5、参数宏在定义时要多加小心,多加括号。
6、:函数调用时,先求出实参表达式的值,然后带入形参。而使用带参的宏只是进行简单的字符替换。在考虑效率的时候,可以考虑使用宏
7、在宏定义代码块时加上do
while(0)
和函数相比,使用宏的不利之处在于每次使用宏时,一份宏定义代码拷贝将插入到程序中,除非宏非常短,否则使用宏可能会大幅度增大代码长度。但和函数相比有一些任务使用函数无法实现比如:
#define
MALLOC(n,type) ((type*)malloc((n)*sizeof(type)))
int
*pi=MALLOC(25,int);
这个宏的第二个参数是一种类型,它无法作为函数参数进行传递
这个宏替换完成之后:
int *pi=((int*)malloc((25)*sizeof(int)));
4 带副作⽤的宏参数
当宏参数在宏定义中出现次数超过⼀次时,如果这个参数具有副作
⽤,那么当你使⽤这个宏时就可能出现危险,导致不可预料的结果。副作
⽤就是在表达式求值时出现的永久性效果。例如,下⾯表达式
x + 1;
可以执⾏⼏百次,他每次获得结果都是⼀样的,这个表达式不具有副作
⽤。但是
x ++;
就有副作⽤:它增加x的值。当这个表达式下⼀次执⾏时,他将产⽣⼀个
不同的结果。MAX宏可以证明具有副作⽤的参数所引起的问题。
#define MAX( a, b ) ( (a) > ( b) ? (a ) : (b) )
int x = 5;
int y = 8;
int z = MAX( x++, y++);
printf("x = %d, y = %d, z = %d\n" , x, y, z );
可以用LINUX指令查看一下它的与处理结果
即它在进行选择时对y又进行了一次++
故y++执行了两次最后结果为10,其实比并不符合我们预期的结果故MAX宏可以证明具有副作⽤的参数所引起的问题。
3、理解编译链接整个过程和详细的每个过程
从C语言转换成机器能够识别的二进制需要如下几个过程
预处理(宏替换,去注释,头文件按照路径展开,条件编译)-->编译-->汇编-->链接
以这个程序为例
#define
MAX(a, b) ((a) > (b) ? (a) : (b))
#include<stdio.h>
int main()
{
int x = 5;
int y = 8;
int z = MAX(x++, y++);
printf("x = %d, y = %d, z = %d\n", x, y, z);
return 0;
}
~
先来看预处理之后的结果:使用gcc
-E www.c -o ww.i 和vim.i指令
接下来进行编译使用gcc
-S www.i -o www.s 和vim.s指令
接下来在进行汇编使用gcc
-c www.s -o www.o 和vim www.o指令
最后是链接过程采用gcc
www.o -o www 和vim www指令
__FILE__ :进行编译的文件
__LINE__ :文件当前行号
__DATE__ :文件被编辑的周期
__TIME__ :文件被编辑的时间
# :使用预处理器将宏参数转换成一个字符串
##:将位于它两边的符号连接成一个符号。
示例 :
__FILE__ :用LINUX指令来观察它的预处理结果和最后输出的结果
#include<stdio.h>
int main()
{
printf("file:%s\t,line:%d,date:%s,time:%d\n",__FILE__,__LINE__,__DATE__,__TIME__);
return 0;
}
__LINE__ ,__DATE__ ,__TIME__也是同理,因此将它们一次写完观察预处理之后的结果即gcc -E mytest.c -o mytest.i,也可以直接观察程序运行完之后的结果即gcc mytest.c 然后用./a.out观察程序运行完的结果
由以上结果可以看出程序在预处理之后就已经将__LINE__ ,__DATE__ ,__TIME__这些预处理标识符用 ”mytest.c",4,"Nov 3 2016 ","10:24:40"替换了即预处理之后的结果就等同于程序运行完之后的结果。
示例:
'#‘使用预处理器将宏参数转换成一个字符串代码如下:
#define M 10
#define PRINT(FORMAT,VALUE) printf("the value of" #VALUE"is" FORMAT"\n",VALUE);
#include<stdio.h>
int main()
{
PRINT("%d",M);
return 0;
}
预处理之后的结果:
由以上的结果就可以看出在预处理之后宏参数通过’#‘就已经转换成一个字符串,而程序运行完之后的结果也是宏参数M变成了一个字符串。
’##‘使用是将位于它两边的符号连接成一个符号
2、了解宏和函数
1、宏可以非常频繁地用于执行简单的运算,可以进行参数的替换
2、宏可以进行临近字符串的拼接并且可以替换掉其中的一部分
3、对函数中的实参和形参都要定义类型,二者的类型要求一致,如不一致,应进行类型转换;而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个符号代表,展开时带入指定的字符即可。宏定义时,字符串可以是任何类型的数据。
4、宏不可以出现递归
5、参数宏在定义时要多加小心,多加括号。
6、:函数调用时,先求出实参表达式的值,然后带入形参。而使用带参的宏只是进行简单的字符替换。在考虑效率的时候,可以考虑使用宏
7、在宏定义代码块时加上do
while(0)
和函数相比,使用宏的不利之处在于每次使用宏时,一份宏定义代码拷贝将插入到程序中,除非宏非常短,否则使用宏可能会大幅度增大代码长度。但和函数相比有一些任务使用函数无法实现比如:
#define
MALLOC(n,type) ((type*)malloc((n)*sizeof(type)))
int
*pi=MALLOC(25,int);
这个宏的第二个参数是一种类型,它无法作为函数参数进行传递
这个宏替换完成之后:
int *pi=((int*)malloc((25)*sizeof(int)));
4 带副作⽤的宏参数
当宏参数在宏定义中出现次数超过⼀次时,如果这个参数具有副作
⽤,那么当你使⽤这个宏时就可能出现危险,导致不可预料的结果。副作
⽤就是在表达式求值时出现的永久性效果。例如,下⾯表达式
x + 1;
可以执⾏⼏百次,他每次获得结果都是⼀样的,这个表达式不具有副作
⽤。但是
x ++;
就有副作⽤:它增加x的值。当这个表达式下⼀次执⾏时,他将产⽣⼀个
不同的结果。MAX宏可以证明具有副作⽤的参数所引起的问题。
#define MAX( a, b ) ( (a) > ( b) ? (a ) : (b) )
int x = 5;
int y = 8;
int z = MAX( x++, y++);
printf("x = %d, y = %d, z = %d\n" , x, y, z );
可以用LINUX指令查看一下它的与处理结果
即它在进行选择时对y又进行了一次++
故y++执行了两次最后结果为10,其实比并不符合我们预期的结果故MAX宏可以证明具有副作⽤的参数所引起的问题。
3、理解编译链接整个过程和详细的每个过程
从C语言转换成机器能够识别的二进制需要如下几个过程
预处理(宏替换,去注释,头文件按照路径展开,条件编译)-->编译-->汇编-->链接
以这个程序为例
#define
MAX(a, b) ((a) > (b) ? (a) : (b))
#include<stdio.h>
int main()
{
int x = 5;
int y = 8;
int z = MAX(x++, y++);
printf("x = %d, y = %d, z = %d\n", x, y, z);
return 0;
}
~
先来看预处理之后的结果:使用gcc
-E www.c -o ww.i 和vim.i指令
接下来进行编译使用gcc
-S www.i -o www.s 和vim.s指令
接下来在进行汇编使用gcc
-c www.s -o www.o 和vim www.o指令
最后是链接过程采用gcc
www.o -o www 和vim www指令
相关文章推荐
- 预处理标识符,预处理与函数的相关的区别;程序的编译与链接的过程
- GCC 预处理 编译 汇编 链接全过程及其含义 程序的开始不是main函数
- 菜鸟C++精髓学习笔记--C++程序内部执行过程(预处理、编译、链接过程的作用)
- Linux程序编译执行原理之一:预处理-编译-汇编-链接过程分析
- 【Linux】使用vim编写一个程序,gcc查看预处理、编译、汇编、链接过程
- c++基础11:变量的命名约定 头文件的问题 程序的预处理编译链接过程
- EWARM IAR5.4编译链接过程, 程序运行阶段, ICF ilink配置文件
- 读书笔记_深入理解计算机系统_第1章_计算机系统漫游 (代码编译链接详细过程)
- C++程序从编译到链接然后再到调用的整个过程
- c语言编译预处理和条件编译执行过程的理解
- 简单汇编程序编译链接执行过程
- 程序的预处理、编译、汇编和链接
- VC++程序编译链接的原理与过程
- VC++程序编译链接的原理与过程(QQ dhms)
- gcc 从语言编译全过程 预处理---->编译---->汇编----->链接
- GCC编译的四个过程 预处理 编译 汇编 链接
- gcc 编译的四大过程(预处理-编译-汇编-链接 )
- C++主流预处理,编译和链接过程
- C程序的编译和链接过程
- c++程序编译过程总结(个人理解)