• 源代码到可执行程序的过程详解:预编译、编译、汇编、链接


    1、gcc编译过程分解

    在这里插入图片描述

    (1)首先是将.c源文件和.h头文件经过预编译(cpp是预编译器),得到.i文件,主要是进行的一些替换工作;
    (2)将.i文件经过编译器(gcc)处理,得到.s汇编文件,现在文件内容已经从C语言编程了汇编语言
    (3).s汇编文件经过汇编器(as)处理变成.o文件,此时的.o文件已经是二进制文件;
    (4)最后将所有.o文件和依赖的静态库、动态库通过链接器(ld)生成可执行程序a.out;

    2、预处理

    //预编译得到.i文件的指令
    gcc -E hello.c -o hello.i
    //或者
    cpp hello.c > hello.i
    
    • 1
    • 2
    • 3
    • 4

    预编译主要处理源代码文件中以"#“开头的预编译指令,比如”#include",主要规则如下:
    (1)将所有的"#define"删除,并且展开所有的宏定义;
    (2)处理所有的条件编译指令,比如"#if"、“#ifdef”、“#elif”、“#else”、“#endif”;
    (3)处理"#include"预编译指令,将被包含的头文件插入到该预编译指令的位置,注意这个过程是递归进行的,也就是头文件内部还可以包含头文件;
    (4)删除所有的注释,就是用"//“和”/* */开头的";
    (5)添加行号和文件名,以便在编译、运行时在显示报错信息、警告信息、调试信息时可以显示行号和文件名;
    (6)保留所有#pragma编译器指令,因为编译器要使用他们;
    总结:预编译后不再有宏定义、头文件、条件编译等,一切都是确定的代码;我们可以用预编译来判断宏定义是否正确、头文件是否包含正确等;

    3、编译

    //编译得到.s文件的指令
    gcc -S hello.i -o hello.s
    //或者
    gcc -S hello.c -o hello.s
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    编译的作用是实现高级语言到汇编语言的转换,详细步骤如下:
    (1)扫描:主要是进行词法分析,将源代码看做一个一个的记号,然后将记号进行分类,一般分为如下几类:关键字、标识符、字面量(数字、字符串等)、特殊符号(加号、等号等);
    (2)语法分析:语法分析会生成语法树,上一步的扫描只是对单个符号进行处理,而语法分析则是要将各个符号结合起来分析,形成表达式。比如:语法分析需要处理运算符的优先级问题(比如乘法的优先级高于加法);"*"在C语言中就可以表示乘法也可以表示指针解引用,语法分析就需要对此作出判断;
    (3)语义分析:语法分析解决了代码之间怎么结合的问题,语义分析需要判断这样结合是否有意义,语法正确并不代表语义上也正确。比如:对两个指针变量做乘法运算是没有意义但符合语法;不同类型之间的变量做强制转换时,涉及的隐式转换就是语义分析阶段处理的;将int型变量赋值给指针变量,编译时报类型不匹配也是语义分析阶段处理的;
    (4)源代码优化:在源代码级别进行优化处理;比如:代码中有"2+6"这样的表达式,在优化后直接变成8。实际的代码优化过程比较复杂,还涉及中间代码的生成,这里就不再做分析;
    (5)代码生成和目标代码优化:将上一步产生的中间代码转换成目标机器代码,这是和具体的目标CPU相关的;

    4、汇编

    gcc -c hello.c -o hello.o
    或者
    as hello.s -o hello.o
    
    • 1
    • 2
    • 3

    (1)汇编的作用:将汇编语言翻译成机器能识别的二进制;
    (2)将每一句汇编语言翻译成机器指令(二进制指令),可以理解成汇编指令和机器指令有对照表;
    (3)汇编语言是和具体的CPU硬件关联。同样的代码,不同架构CPU的汇编器最终得到的二进制是不同的,这也是为什么嵌入式开发中,需要用CPU对应的交叉编译工具链去编译可执行程序的原因;

    5、链接

    在这里插入图片描述

    (1)在经过上述步骤后生成了.o文件,到目前为止都是以单个文件来处理的,文件之间或者是库之间的调用关系还没有处理;
    (2)我们把链接过程理解成拼图的过程。比如:A模块引用了B模块的fun函数,在编译A模块时检测到调用了fun函数,但是A模块中并没有发现fun函数的实现,于是就会在调用fun函数的地方做个标记(相当于缺了一块),当链接的时候就去所有模块里查找是否有fun函数的实现;恰好在B模块中找到fun函数的实现(如果在多个模块找到fun函数定义就报重复定义的错误),于是就把B模块fun函数和A模块调用的地方链接起来;
    总结:链接过程就是把所有.o文件根据函数调用关系拼接在一起,形成可执行程序;

  • 相关阅读:
    找出字符串中第一个匹配项的下标
    java面试清单和书籍推荐 五颗星五颗星
    Java版本spring cloud + spring boot企业电子招投标系统源代码
    3D目标检测进展综述(论文笔记)
    cc链学习总结
    JZ31 栈的压入、弹出序列
    技术分享 | 接口自动化测试之JSON Schema模式该如何使用?
    分布式通信框架
    springboot+mp:瑞吉外卖
    数据分析面试题(2023.09.08)
  • 原文地址:https://blog.csdn.net/weixin_42031299/article/details/126911552