理解程序编译预处理与链接过程

时间:2022-08-19 01:00:00

首先熟悉一些预处理标识符

__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指令

理解程序编译预处理与链接过程