您的位置:首页 > 编程语言

6.1.预处理都做了什么

2016-07-15 20:33 369 查看
由我们写好的源代码到CPU可以认识并执行的二进制中间发生了很多事,我们来深入探讨一下C语言的预处理。

1.从源代码到可执行程序的过程

(1)源代码.c文件先经过预处理器,生成一个中间文件.i文件

(2).i文件经过编译生成汇编.s文件

(3).s的汇编文件经过汇编器生成.o的目标文件

(4).o的目标文件经过链接器生成.elf可执行程序

每一步都有其相应的工具,预处理有预处理器,编译有编译器,链接有链接器,这些工具合在一起叫做编译工具链。在windos上的编译器,这些都被所谓的编译器屏蔽了。只留出一个编译的功能,其实叫编译器是不准确的。在Linux中,gcc就是一个编译工具链。gcc包含预处理器,编译器,汇编器,链接器等。

2.gcc中只预处理不编译的方法

gcc xx.c -o 由源码到可执行程序

gcc xx.c -c -o 只编译不连接,生产.o的目标文件

gcc -E xx.c -o xx.i 只预处理产生的中间文件

看只预处理的中间文件有利于我们调试和研究其预处理过程,还记得我们之前说过的#define和typedef的细节差异吗?现在我们就来分析一下,并且看看预处理是个什么过程。

#include<stdio.h>

#define pchar char*
void main(void)
{
pchar p1,p2;
}

//进行预处理操作,gcc -E xx.c -o xx.i 产生中间文件

#include<stdio.h>

void main(void)
{
char* p1,p2;
}


分析:

(1)经过预处理后,原来的#define 代码不见了,直接在其所在位置进行替换

(2)#define的替换是简单的替换,就是把所有的pchar 用char*替换

(3)我们看p1,p2的类型。p1shi char*类型的,而p2只是char类型的

再来看typedef的使用

#include<stdio.h>

typedef char* pchar
void main(void)
{
pchar p1,p2;
}

//预处理后

typedef char * pchar;

void main(void )
{

pchar p1,p2;

}


我们发现没有变化。也就是说typedef重定义的类型在预处理阶段是不产生变化的,而是在编译阶段发挥作用的。在编译阶段就把pchar当作普通的char*使用。

3.头文件在预处理阶段发生什么变化?

(1)有两种包含方式 第一种:#include<> 第二种:#include””

第一种是用来包含系统提供的头文件的

第二种是用来包含程序员自己写的头文件的

(2)两种包含的区别

第一种:#include< > 的方式,C语言编译器只会到系统指定的目录(编译器配置的目录)去寻找这个头文件,如果找不到就出错。但是编译器允许使用 - i命令添加更多的目录到该系统目录。(不会到当前目录去找)

第二种方式:#include”“的方式,编译器先当前目录下去寻找,如果没有找到则到系统指定的目录去找。这种方式也可以包含系统文件。

总结:(1)虽然可以使用“”来包含系统头文件,但是一般来说,遵从一定的规则。系统文件用<>,自己写的用“”,如果自己写的一大群头文件,也使用<>,把该目录添加到编译器指定的目录去即可。

(2)(头文件包含的实质)在1.c中包含1.h,就是把1.h中的代码原封不动的写到1.c中#include<>语句的原地方去。

—-1.c文件—-

# nclude"1.h"
main()
{
c = a + b;
printf("c = %d\n",c);
}


----1.h文件----

int a = 1;
int b = 2;
int c = 0;


预处理的命令:gcc -E 1.c -o 1.i

结果

# 1 "1.c"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "1.c"
# 1 "1.h" 1
int a = 1;
int b = 2;
int c = 0;
# 2 "baohan.c" 2
main()
{
c = a + b;
printf("c = %d\n",c);
}


分析:

(1)前面多了很多我们不认识的东西,不用管它,那是给编译器看的。

(2)在1.c 的#include”1.h”这句代码处,把1.h中的代码全部拷贝到了这里。

(3)需要注意的是,我们写的代码进过预处理后产生了一个.i文件,所有的改动都是在.i文件中。原来的.c文件补货被改动的。

4.注释在预处理阶段的变化

两种条件编译#iddef 和#if( )

#include<stdio.h>
//声明函数
void test(void);
void main()
{
//定义变量
int a = 1;
int b = 2;
}


经过预处理后

#include<stdio.h>

void test(void);
void main()
{

int a = 1;
int b = 2;
}


我们发现所有的注释都被删除了。

总结:

(1)注释是给人看的,编译器不用看。

(2)在预处理阶段,预处理器会全部删掉注释。也就是说在编译的时候,编译器根本不知道什么是注释。

(3)预处理器改的是预处理后的一个中间文件.i,而不是源代码.c

5.条件编译在预处理阶段发生了什么?

#include<stdio.h>

#define test 3 //宏定义 test 为3

void main(void)
{

#ifdef test 3   //判断 test是不是被宏定义为3,是
printf(" test 3  !\n");

#else           //不是

printf("else !\n");

#endif

}


预处理后

预处理后被包含进来的stdio.h没有写出来,太长了。

void main(void)
{

printf(" test 3  !\n");

}


分析:在预处理阶段,条件编译,会判断哪个条件成立,把条件成立的那个语句留下,不成的条件的那个被舍去。(在.i文件中,不是.c文件)

再来看第二种条件编译 #if()

#include<stdio.h>

#define ok 1

main()
{
#if(ok)

printf("ok\n");

#else

printf("not ok\n");

#endif
}

//预处理后:

main()
{

printf("ok\n");

}


总结:

第一种#ifdef 是判断是否该符号被宏定义,在舍弃条件未成立的那一部分

第二种#if() 和if()一样。括号中是一个逻辑真假值。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息