关于编译运行过程以及链接的遐想
———- android培训、java培训、期待与您交流! ———-
引子
我们来看一段简单的C代码:
#include <stdio.h>
int main(int argc, const char *argv[])
{
printf("Better late than never!");
return 0;
}
上面这段代码如果要想被执行就要经过 编译->链接->执行这些过程下面我们来具体说明一下:
编译
实际上我们通常说的编译阶段其实是分为编译前的预处理和编译两个阶段:
1.预处理
首先把源文件中的#include 和 #define后面的代码在整个程序被编译前做预处理
也就是把程序中的每一处用到宏定义的地方将宏定义中定义的值或者函数
对其进行代码替换,和把include 后面所包含的系统库文件和
自己写的头文件(.h文件)包含进本文件的该预处理语句所在的对应位置
2.编译
将经过预处理后的源文件进行语法检错和忽略注释的编译最后生成.o文件
(windows VC编译环境下生成.obj文件)
PS: 本步骤只做语法检错,不会检查逻辑是否出错跟不会检查数组是否越界
链接
3.链接
把经过编译后的目标文件(.o文件/.obj文件)和函数库进行链接,
如果没有指定输出可执行文件的名字的情况下,会生成可执行文件a.out
(VS环境下生成扩展名为.exe的可执行文件)
PS: {
我的理解是预处理中只是将用到的函数库的头文件
(一些宏的定义和函数的声明以及部分用带参数的宏定义)包含了进来
而那些只在函数库头文件作出声明的函数的实现文件已经被封装成了2进制的.o文件
所以此时也就是链接的时候才会轮到他们出场和我们的.o文件完成拼接
生成最终计算机能真正执行的2进制文件(即扩展名为.exe 或a.out的可执行文件)
}
看下面的代码
int sum(int a, int b);
int main(int argc, const char *argv[])
{
sum(3, 4);
return 0;
}
上面这段代码真实的反映了一个事实:
事实
事实—如果只有函数的声明,而没有函数的定义,那么程序将会在链接时出错。
证明—编译阶段只是将程序翻译成2进制的代码到链接阶段会根据函数的使用
指向函数实现的2进制代码处如果连接器没有找到就会发生链接错误
依照这个逻辑这就能说明
链接的作用是:
将所有将要交由计算机执行本程序中的函数所调用的实际实现的2进制代码相串、链接起来
{
也就是说我们所谓的需要链接的库函数其实就是我们通过include引用进来的头文件
中声明了的没有通过宏定义的方式实现的,被我们程序中所调用到的函数的实现
并且这些在引用前就已经是2进制文件了也就是说
①如果我们程序调用了printf函数
②那么我们就需要引用stdio.h文件,因为这里面有printf函数的声明,或者有它带参数的宏定义
③如果没有那么就一定在编译环境所提供的类似于stdio.o文件中,因为从始至终我们从来没有编译过
stdio.c文件
}
运行
4.运行可执行文件
键入./a.out运行可执行文件即可
(windows 环境下双击生成的扩展名为.exe的可执行文件即可)