• 【C语言】.c源文件从编译到链接生成可.exe执行程序的过程


    推荐看《程序员的自我修养 - 链接、装载与库》更深入了解相关内容

    1. 过程概览

    编译到链接是c语言的翻译环境,c语言还有执行环境

    在这里插入图片描述

    1. 组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code);
    2. 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序;
    3. 链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。

    2. 编译与链接

    在这里插入图片描述

    2.1 预编译/预处理

    总的来说都是文本操作,注释被删除替换成一个空格;#define定义的符号也会被删除,其它所有出现这个符号的地方都被替换成所代表的常量;头文件所有函数实现都会包含进来,总共大概一两万行,最后才是自己写的代码。

    预编译后会产生一个对应的.i文件,不过要说明的是这个预编译的步骤在vs中可以看到,但后面的步骤基本只能在linux gcc编译器,使用指令一步步看。
    在这里插入图片描述
    在windows vs中查看预编译后产生的文件:
    在这里插入图片描述
    选择是然后运行程序,实际程序会编译失败,因为仅仅只进行了预处理这个步骤,不过可以在项目文件下的X64/Debug下看到一个对应的.i文件。
    在这里插入图片描述
    在这里插入图片描述

    在linux下执行预处理选项指令 gcc -E test.c -o test.i(-E代表只进行预处理,-o test.i代表指定输出到这个文件),预处理完成之后就停下来,预处理之后产生的结果都放在test.i文件中。

    2.2 编译

    在linux下执行编译选项指令 gcc -S test.c,编译完成之后就停下来,结果保存在test.s中。

    编译阶段会将c代码转换为汇编代码,汇编大致会经过四个阶段:

    1. 词法分析;
    2. 语法分析;
    3. 语义分析;
    4. 符号汇总。

    比如图中左边的这段代码,对应右边编译器一个个符号分析它们分别代表什么。
    在这里插入图片描述
    语法分析阶段会产生一个语法树:
    在这里插入图片描述
    语义分析应该是根据语法树进行,我也不是很了解。

    符号汇总这个阶段很重要,编译器会将每个.c文件中的全局符号(函数名、全局变量)汇总起来,后面的汇编和链接都会涉及到符号的处理。

    2.3 汇编

    汇编阶段是将编译阶段产生的.s文件(汇编代码)转成二进制机器码文件。在linux下执行汇编指令 gcc -c test.c,汇编完成之后就停下来,结果保存在test.o中。

    在这个阶段会根据前面编译阶段的符号汇总,产生一个符号表,如:
    在这里插入图片描述
    对于右边申明的Add函数,在符号表中的地址是无效的,后续不会使用这个无效的地址,而是使用左边的地址,符号表在连接阶段会使用。

    2.4 链接

    链接阶段链接器(linker)会链接所有产生的.obj / .o目标文件,以及链接库生成合并成一个二进制的.exe可执行程序文件,这个阶段会经历合并段表、合并符号表操作。

    gcc编译器生成的二进制目标文件和二进制的可执行文件,都是按照elf文件形式组织的,合并段表要做的就是将所有.o文件中相同数据段进行合并在一起;每个文件产生的符号表最后也会合并在一起,符号表中无效的地址会被舍弃,使用有效的地址,因为链接器链接查找函数都是根据符号和地址的匹配关系去找的,不然没法找到;最后生成一个.exe文件。

    add.c和test.c的符号表,其中0x0000,无效的地址值会被舍弃:
    在这里插入图片描述

    如果add.c中没有Add函数,那么符号表中没有对应的函数名Add和地址,那么会连接失败:
    在这里插入图片描述

    3. 执行/运行环境

    1. 程序必须载入内存中。在有操作系统的环境中:一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
    2. 程序的执行便开始。接着便调用main函数。
    3. 开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使用静态(static)内存,存储于静态内存中的变量在程序的整个执行过程一直保留他们的值。
    4. 终止程序。正常终止main函数;也有可能是意外终止。
  • 相关阅读:
    Netgear R6700v3 1.0.4.102(CVE-2021-27239)
    jupyter notebook代码自动换行,超过一行长度自动换行,不用左右滑动
    14李沐动手学深度学习v2/权重衰退简洁实现
    开源之夏 2023 | Databend 社区项目总结与分享
    [uni-app] scroll-view中吸顶的简单做法 - overflow-y: auto;
    腾讯音乐基于 Apache Doris + 大模型构建全新智能数据服务平台
    [GHCTF 2024 新生赛]ezzz_unserialize
    蓝牙技术|蓝牙在物联网产品上的功能,特别是苹果Find My中的应用
    云原生中间件RocketMQ-核心原理之高可用机制
    每天进步一点点 --- Guava
  • 原文地址:https://blog.csdn.net/m0_52602233/article/details/133519054