当前位置:网站首页 > C++编程 > 正文

编译libc(编译libc库)



在这里插入图片描述


   在 ANSI C 的任何⼀种实现中,存在两个不同的环境,如下:

  1. 翻译环境:在翻译环境中,会通过编译和链接两个大步骤,其中编译又分为了预处理(预编译)、编译和汇编,将源代码转换为可执⾏的机器指令(⼆进制指令),生成可执行程序
  2. 运行环境:即执行环境,在运行环境中会执行可执行程序,并输出结果

预处理

  1. 将所有的 #define 删除,并展开所有的宏定义,比如使用宏定义了一个常量,我们一般会这样写:
 
  

   那么经过预处理之后,#define N 100这条语句就会被删除,并且这个宏定义将会被展开,在这里就是将所有N替换成100,如下:

 
  
  1. 处理所有的条件编译指令,如: #if、#ifdef、#elif、#else、#endif等等,在下一篇预处理详解我们会讲到,这里简单介绍一下
  2. 处理#include 预编译指令,将包含的头⽂件的内容插⼊到该预编译指令的位置,比如包含头文件stdio.h,那么就会将头文件stdio.h中的所有内容插入到原位置
       虽然VS不会生成.i的文件,但是我们还是可以看到头文件的内容,首先使用#include包含stdio.h,然后使用ctrl+单击鼠标,就可以看到stdio.h这个头文件的内容,有两千多行的代码,在预处理后,就会全部插入到我们的源文件中来

  3. 经过预处理后,会删除所有的注释,所以我们写了注释才不会影响代码的运行,因为在我们正在编译前,就已经把它删除了
  4. 添加⾏号和⽂件名标识,⽅便后续编译器⽣成调试信息等
  5. 保留所有的#pragma的编译器指令,它可以保证我们不重复包含头文件

编译

   当我们进行预处理后,就来到了编译阶段,编译过程就是将预处理后的⽂件进⾏⼀系列的:词法分析、语法分析、语义分析及优化,⽣成相应的汇编代码⽂件
   我们现在就用一句代码为例,看看词法分析、语法分析、语义分析的大致实现思路,如下:

 
  
  1. 词法分析:首先代码就来到了词法分析,这个阶段会将源代码程序输⼊扫描器,扫描器的任务就是简单的进⾏词法分析,把代码中的字符分割成⼀系列的记号(关键字、标识符、字⾯量、特殊字符等)
       上⾯程序进⾏词法分析后得到了16个记号,然后就会生成一个记号表,如下图:
    在这里插入图片描述
       在这个阶段会简单的标记每个记号,然后生成一个记号表,这个符号表在后面链接的地方还会用到,我们到时候会再来说它的另一个作用








  2. 语法分析:来到语法分析阶段后,会将源代码放入语法分析器,将对扫描产⽣的记号进⾏语法分析,从⽽产⽣语法树,这些语法树是以表达式为节点的树,如图:
    在这里插入图片描述
       在这个阶段,我们代码的意思基本上就明确了,相当于在语法分析阶段,会把要组合的记号组合起来,明确这些记号的基本含义
       并且在图上我们也可以看出来,这颗语法树的节点是一个又一个的表达式组成,如果在这个时候出现简单的语法错误就可以发现,比如少写一个括号,就不能像这样构成一个以表达式为节点的语法树,程序就可能会报错








  3. 语义分析:经过词法分析和语法分析后,由语义分析器来完成语义分析,即对表达式的具体语法层⾯的分析
       编译器所能做的分析是语义的静态分析,静态语义分析通常包括声明和类型的匹配,类型的转换等,这个阶段会报告错误的语法信息,如下图:
    在这里插入图片描述
       在语义分析这个阶段就能基本明确程序的语法含义了,如明确了类型、类型的转换等信息,而语法分析中只是对记号表中的记号进行了组合和简单的翻译
       在这个阶段已经可以判断表达式之间的关系了,比如整型加整型是整型,整型赋值给整型等等,并且在这个阶段可以找出语法错误
       比如在赋值表达式的左边算出的结果是一个浮点型,而左边算出来了的却是一个整型,那么就会进行强制类型转换,如果表达式左边是整型,而右边是结构体,就会报错
       而且最关键的一点是,我们通过语义分析已经知道了代码的含义,那么把它翻译成汇编代码也不是难事了,所以在这个阶段会正在将源代码翻译成汇编代码,并做相关的优化

















汇编

 
  

   test.c

 
  
  1. 全局符号(Global symbols):由当前模块定义并能被其他模块引用的符号(指不带static的全局变量)
  2. 外部符号(External symbols):由其他模块定义,并能被当前引用的全局符号
  3. 局部符号(Local symbols):仅由当前模块定义和引用的本地符号。例如,定义的static函数和变量
    接下来我们可以简单的画出add.c中的符号表,在画的时候我们要注意,在add.c中并没有外部符号的引用,出现的符号add拥有正常的声明和定义,所以会直接分配一个虚拟地址,如图:
    在这里插入图片描述




   前⾯我们⾮常简洁的讲解了⼀个C的程序是如何编译和链接,到最终⽣成可执⾏程序的过程,其实很多内部的细节⽆法展开讲解。⽐如:⽬标⽂件的格式elf,链接底层实现中的空间与地址分配,符号解析和重定位等,如果你有兴趣,可以看《程序员的⾃我修养》⼀书来详细了解

   在运行环境中有几个要点,我们只需要简单了解一下:

  1. 程序首先必须载⼊内存中。在有操作系统的环境中:⼀般这个由操作系统完成。在独⽴的环境中,程序的载⼊必须由⼿⼯安排,也可能是通过可执⾏代码置⼊只读内存来完成
  2. 随后程序开始执行。会直接调⽤main函数
  3. 开始执⾏程序代码。这个时候程序将使⽤⼀个运⾏时堆栈(stack),存储函数的局部变量和返回地址。程序同时也可以使⽤静态(static)内存,存储于静态内存中的变量在程序的整个执⾏过程⼀直保留他们的值
  4. 终⽌程序。正常终⽌main函数;也有可能是意外终⽌
到此这篇编译libc(编译libc库)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • 数组方法slice改变原数组么(数组改变本身的方法)2026-04-08 17:27:08
  • bbc是哪个国家的新闻媒体(bbc是哪个国家的记者)2026-04-08 17:27:08
  • apc连接头(apc 接口)2026-04-08 17:27:08
  • cnn是什么网络(cnn 是什么)2026-04-08 17:27:08
  • console线和串口线一样吗(console线usb口接线图)2026-04-08 17:27:08
  • twincat3扫描不到设备(twincat扫描不到io)2026-04-08 17:27:08
  • kubelet命令(kubectl命令详解)2026-04-08 17:27:08
  • git clone 加上账号密码(git clone密码错误)2026-04-08 17:27:08
  • git clone 和git checkout区别(git clone 和git pull)2026-04-08 17:27:08
  • cpu参数对比工具有哪些(cpu参数对比工具有哪些)2026-04-08 17:27:08
  • 全屏图片