• 33.0、C语言——C语言预处理(1) - 翻译环境详解


    33.0、C语言——C语言预处理(1) - 翻译环境详解

    程序的 翻译环境 和 执行环境

    在ANSI C的任何一种实现中,存在两个不同的环境;

    第 1 种是翻译环境,在这个环境中源代码被转换为可执行的机器指令;

    第 2 种是执行环境,它用于实际执行代码;

    详解:翻译环境 = 编译 + 链接

            在我们的项目中 可能会出现多个 源文件,那么每一个源文件都会被系统单独的当成一个单元去单独处理;

            源文件在经过 编译器 处理之后 会产生一个目标文件也就是 .obj 文件,然后等待每一个目标文件都通过 链接器链接库 处理之后,会将所有目标文件链接到一起,然后产生一个可执行程序 .exe;

            - 组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code);

            - 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序;

            - 链接器同时也会引入标准 C 函数库中任何被该程序所用到的函数,而且他可以搜索程序猿个人的程序库,将其需要的函数也链接到程序中;

    那么其实编译又可以细分为三个过程 ->

            1. 预编译

            2. 编译

            3. 汇编

    详细介绍:

    其实这三个过程在我们的 vs编译器 中不太好体现出来,还是用 Linux 比较容易理解;

    预处理阶段 ->    

            1. 预处理 选项 gcc -E test.c -o test.i 编译完成之后就停下来,预处理之后产生的结果都放在 test.i 文件中;

            那么在进行预编译处理的时候,相当于把 #include 执行了,将所有的头文件包含的内容添加到了我们的 源文件 中; 

            其实不仅仅只是将头文件包含进了源文件,还做了一些其他的事情,比如说 对源文件的注释删除(使用空格去替换注释),因为注释的信息对编译器来说没有任何意义;

            还有比如说 #define 定义的一些常量会在预编译阶段,将这些常量全部替换成 值 ,比如说 #define max 100 那么在预处理阶段会将程序中 所有的 max 换成 100;

            其实总的来说,就是一些文本的操作,文本的包含文本的替换啥的;

    编译阶段 ->

            2. 编译选项 gcc -S test.c 编译完成之后就停下来,结果保存在 test.s 中;

            将我们的 C代码 翻译成了 汇编代码,那么详细的的来说就是做了以下一些动作 ->

            1. 语法分析

            2. 词法分析 ( 涉及到编译原理,这里不细说 )

            3. 语义分析

            4. 符号汇总 (比如说一些 函数名、全局变量等都汇总起来)

    汇编阶段 ->

            3. 汇编 gcc -c test.c 汇编完成之后就停下来,结果保存在test.o 中;

            将之前编译好的汇编代码,转换成 object 目标文件【  当然这个目标文件我们依然看不懂,因为他是将汇编代码转换成了二进制指令 / 代码 】;然后所有的目标文件通过链接器生产可执行程序; 

            当然汇编阶段还做了一件事情 -> 形成符号表 ,符号就是之前在编译阶段汇总的符号,那么在汇编阶段会将符号以及符号对应的地址存储到符号表中;

    例如像以下代码 ->

    这里要汇总的符号有两个 一个是 main 一个是 add;

            在该源文件中我们确切的知道 main() 函数式存在的,所以可以有一个相对应的地址存到符号表中;但是 add() 函数只是声明,并不知道他是否确切存在,所以会存入一个无意义的地址;

    链接阶段 ->

    链接器要做以下两件事请 ->
    1. 合并段表;

    2. 符号表的合并和重定向;

    1. 那么先来说一下什么是:合并段表 ->

            首先每个目标文件中会分为不同的段,每段会存放一些程序的数据、代码等;不过每个目标文件的段结构、格式是一样的【这种格式被称作 elf 文件格式,感兴趣的可以百度一下】,只是每段存放的东西不一样;

            经过链接阶段时,首先会把每个目标文件中相对应的段位置里的数据合并到一起,最终形成一个目标文件;

    2. 什么是符号表的合并和重定向 ->

            在经过编译阶段后,会形成 符号表,符号表中存放了符号以及他们的地址;那么重复的符号会合并,合并之后如果他们的地址不同则取 有效地址 存入到合并后的符号表中;

    符号表的作用是啥呢?

            比如说符号表里现在存放着一个函数 Add() 的 符号 Add 以及他的地址,但是这个函数根本就没有定义,只是声明了一下;那当我们在程序中调用这个 Add() 函数的时候,就会去符号表中查找有没有这个符号 Add,查找后发现确实有然后根据他存放的地址去调用Add() 函数,但是发现这个地址是一个无效地址,根本找不到那么就导致函数调用失败了;

            当我们的 编译阶段 通过 且 链接阶段 也通过后那么可执行程序就 成功生成了【其实可执行程序也是 elf 文件格式】,那么到这里我们的翻译环境就基本结束了; 

  • 相关阅读:
    JavaScript的闭包的详细讲解
    Go结构体&接口&反射
    图解-虚拟机使用NAT方式连网
    2022牛客多校(一)
    QFramework 常用
    Python自然语言处理的力量:NLTK库介绍
    异常是怎么被处理的?这题的答案不在源码里面。
    MySQL增删改查语句练习(grade表,student表,subjects表,result表)
    ORA-00600之数据库内部BUG 22114696
    Ubuntu install vncserver
  • 原文地址:https://blog.csdn.net/m0_52433668/article/details/127034620