面试冲刺:02---从源代码到可执行程序的步骤(预处理、汇编、编译、链接)

一、程序编译原理

  • 一个文件从源代码到可执行程序会经历以下4个步骤:
    • 预处理:在程序运行(main函数执行)之前,修改源码,主要处理代码中的#include头文件和#define宏定义代码(将程序运行时用到的#include头文件中的代码和#define宏定义的代码进行替换),最后生成一个.i文件(.i文件里面是替换代码之后的代码文件)
    • 编译:此阶段会检查代码的规范性、是否有语法错误等。在检查无误后,把.i代码文件进行编译,然后生成一个汇编语言.s文件(.s文件里面都是汇编语言)。此处只进行编译生成汇编代码,而不真正的进行汇编
    • 汇编:此阶段把.s文件翻译成二进制机器指令.o文件。生成的.o文件是二进制文件(直接用文本工具打开看到的将是乱码,我们需要反汇编工具如GDB的帮助才能读懂它),Windows下为.obj文件
    • 链接:此阶段会链接所有的函数、全局变量,将所有的.o文件链接成一个可执行文件(例如hello.c文件调用了printf函数,printf函数存在一个名为printf.o的文件中,而我们必须把printf.o合并到hello.o中。不过有时.o文件太多,链接会很不方便,所以我们会给.o文件进行打包生成静/动态库文件(Windows下为.lib和.dll文件,Linux下为.a和.so文件)

  • 注意事项:
    • 1.在编译时,编译器只检测程序语法,和函数、变量是否被声明。如果函数未被声明,编译器会给出一个警告,但仍可以生成Object File。但是链接时,链接器会在所有的Object File中找寻函数的实现,如果找不到,那到就会报链接错误码
    • 2.一个.c文件只生成一个.i和.s和.o文件,链接时是将所有的.o文件一起链接
    • 3.h文件不直接参与源码编译,.cpp/.c等参与源码编译
    • 4.承上,因为.h文件不参与源码编译,所以在.h文件中不要写带内存的代码
  • 各种后缀名文件的意义:
    • .c:C语言源代码文件
    • .h:程序所包含的头文件
    • .i :C语言源代码文件经过预处理生成的文件
    • .s:.i文件经过编译生成的汇编文件(文件中都是汇编语言)
    • .o:.s文件经过汇编生成的二进制机器执行文件
    • .S:是经过预编译的汇编语言源代码文件

二、演示案例

第一步(预处理):

  • 现在我们有一个源代码文件hello.c

#include <stdio.h>

#define NUM 10

int main()
{
    printf("num is %d\n",NUM);
}
  • 现在我们使用gcc的-E选项将一个.c源文件编译成一个.i文件
gcc -E -o hello.i hello.c

  • 之后查看.i文件的内容,我们知道预处理会将源代码中用到的头文件代码和宏进行替换,此程序中使用到了stdio.h头文件和一个NUM宏,因此会将stdio.h头文件中的代码都包含进来了(可以看到代码变成了八百多行),并且将宏替换为实际的数字(下面也替换了)

第二步(编译):

  • 现在我们使用gcc的-S选项,将.i文件编译成一个.s文件
gcc -S -o hello.s hello.i

  • 接着查看.s文件的内容:可以看到编译阶段会将.i文件中的源代码都替换为汇编代码(此处只进行编译生成汇编代码,而不真正的进行汇编)

第三步(汇编):

  • 使用gcc的-c选项将.s文件汇编为一个.o文件
gcc -c -o hello.o hello.s

  • 之后查看.o文件的内容:.o文件时二进制文件,因此用普通文本软件打开查看都是乱码

第四步(链接):

  • 使用gcc的-o选项将.o文件链接为可执行程序
gcc -o hello hello.o

  • 这个程序就是我们最终可以执行的程序,我们尝试运行这个程序,结果显示运行成功

发布了1300 篇原创文章 · 获赞 828 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/103748147