gcc预处理、编译、链接简述

 

C语言的编译过程主要分成3个阶段:

1:预处理

2:编译

3:链接

 

1 预处理

       该阶段有两个工作,一是将#define 给扩展开来,二是通过#include将.h文件加入到该代码中。

代码中所有用到define一样字符串的位置通过define的规则给一一替换。前一个#define 会影响后一个#define,但是代码中常量字符串不会被替换。

例子:

#define I 6

#define J I*5  //I会被替换成6

 

int main()

{

       int i=J;        //J会被替换成6*5

       char a[30]="dgJ";  //J不会被替换

return 0

}

 

#define 的一些高级运用:

#define MAX(x,y) ((x)>(y)? (x):(y))

对于MAX(5,6)将被替换为((5)>(6)? (5):(6))

但是对于MAX(f(5),f(6))将被替换为:

((f(5))>(f(6))? (f(5)):(f(6)))

函数被多调用,如果函数本身很复杂则得不偿失。

所以可以写成

#define MAX(x,y) ({typeof(x) _x=x;\

                              typeof(y) _y=y;\

                              _x>_y? _x:_y;})

 

       一个块的值(即大括号{})是最后一个表达式的值。

 

#和##在宏定义中的作用

       #define A(a,b) a##b //连接a和b

       #define B(r) #r  //将r变为字符串”r”

 

#include的具体处理

#include的头文件会被依次加入代码中。

如果有递归定义的#include,以前的实现中将无限递归加载,现在的大多实现中解决了这个问题,但是还是最好自己用#ifndef __xxx__的方式来限定一个.h只能被调一次。

#ifndef __XXXXH__

#define __XXXXH__

...

...

...

#endif

 

2 编译和链接

       具体做了什么,自己百度。在此只是说一个很有意思的现象。在链接阶段,即使你在代码头部没有包含标准库的头文件,但是代码中调用了标准库函数,实际链接的过程中,会准确定位到该函数,程序将正确执行。

       看例子就懂了:

#include <stdio.h>

 

int strlen(const char *,int);

 

int main()

{

    int i;

    i=strlen("sdf",5);

    printf("%d\n",i);

    return 0;

}

 

如果没有加int strlen(const char *,int);会报strlen没有声明的错误,所以要加上。

实际上,我没有实现strlen函数,也没有加string.h的头文件,但是程序运行正常(将会调用标准库的strlen())。因为编译阶段,只是简单的一个语法检查,它认为strlen(“sdf”,5)的调用没有问题,因为我在前面声明了这个函数。链接阶段,会搜整个标准库一遍,所以strlen将调用标准库string.h中的strlen()函数。

真实执行中,参数5入栈、参数char *入栈,然后调用标准库string.h里的strlen(const char *)。但是标准库的strlen()明明只有一个参数,会出现执行错误么?答案是不会,因为char *无论是哪种相对栈指针的偏移是一致的,strlen()函数将能正确访问到char *变量,int参数5虽然入栈,但是函数中没有访问它。这一块具体可以看C语言内存管理中的栈区加深理解。

 

posted on 2016-01-07 16:02  zebfff  阅读(362)  评论(0)    收藏  举报

导航