c语言的编译一般会阶段3个阶段的过程: 预编译 编译 链接
预编译阶段要做的事(处理一些以#开头的指令):
1、展开头文件。比如#include<stdio.h>这样的头文件中存在许多的函数定义,预编译要做的事就是把这个头文件中的函数定义全部替换出来。
2、处理宏定义。比如说#define N 3.这样的宏定义在预编译阶段就会把源代码文件中的N全部换成3.
3、处理条件编译语句。包含了两类,头文件中的预编译条件和源代码文件中的条件编译语句
头文件的条件语句:
#ifndef _SIZE_T
#define _SIZE_T
typedef
unsigned
size_t
;
#endif
一般的格式是
#ifdef
#elifdef
#else
#endif
源代码的条件语句:
#if 0
printf("hello");
#else
printf("word");
#endif
编译阶段要做的事:
1、调整源代码文件中每一行的格式,在行尾加上换行字符。 在这一过程中会优化源代码字符中每一行的代码,去掉多于的换行,并且会把三字符组替换成单字符
2、此源代码文件被分解成预处理器的记号和空格符, 在这过程中注释会被转换成一个空格符。
形式如下:
1 #include <stdio.h>
2
3 int main()
4 {
5 printf("hello word\n");
6 }
7 所谓的记号就是在语意上存在一定的逻辑的符号。
8
9 上面的printf函数会被分成:
10 printf
11 (
12 "hello word\n"
13 )
14 ;
3、执行上面的记号序列
4、转换字符成运行字符集中对应的字符
5、 编译器分析记号产生的序列,产生对应的机器码
通过以上的编译阶段我们可以发现,编译阶段要作的事就是优化源代码,并且把与源代码字符集中的字符转换成运行字符集中的字符。其目的是查找语法错误。
通过gcc -S test.c -o test.s 可以产生一个对应的汇编语言的文件
通过gcc -c test.s -o test.o 产生一个二进制文件,但是此二进制文件不一定可以执行
通过gcc test.o -o test 生成一个可执行的二进制文件
可以直接从源代码文件编译成可执行的二进制文件:
gcc test.c -o test 不要写成 gcc -c test.c -o test
编译阶段主要做的事就是把翻译单元中的内容生成机器码,(一般先生成汇编语言)
链接阶段主要做的事就是把所有的机器码组合起来,为每一条汇编指令提供一个地址,然后在运行阶段把这些指令放到内存中去执行。
链接阶段:
链接阶段的主要任务就是展开头文件,调用标准链接库里面定义的函数(这些函数是可执行的函数,不能看见源代码)
,有些函数是静态的去调用,就是直接把函数体写到源文件中,但是有些函数是动态链接的,就是在链接阶段只判断这个函数的存在性,分配一个地址,在执行时在来替换函数。
所以就存在了在运行阶段有时也会调用动态链接库。