C语言编译和链接,带你逐步了解程序执行过程,相当于练习内功!!!

1.翻译环境和运行环境

  1. 代码都是文本信息,计算机是不能直接识别的,计算机可以直接执行的是二进制指令
  2. C语言代码是需要经过处理的,怎么处理呢?是通过编译器处理的。
  3. 在ANSI C(标准c)的中有两种不同的环境。
    1. 翻译环境,这个环境中源代码会被翻译为可执行的(机器指令)二进制指令
    2. 运行环境,执行代码
  4. 在调试时运行的程序,是通过翻译环境生成的后缀.exe的文件

2.翻译环境

  1. 在写项目的时候会有多个.c文件,他们通过每个都通过编译器的处理,各自都生成了一个目标文件
  2. 多个目标文件和链接库一起通过链接器处理生成的可执行程序

2.1预处理(预编译) test.i

  1. gcc test.c -E -o test.i
    1. -E 意思是在预处理阶段停下来
    2. -o 想让它生成的文件是 test.i
  2. 编译有可以分为细分为三个过程 预处理 ——> 编译 ——> 汇编
  3. 完整的过程:C代码 ——> 预处理 ——> 编译 ——> 汇编 ——> 链接 ——> 可执行程序
  4. 在预处理阶段,源文件和头文件会被处理成后缀为 .i 的文件。
  5. 预处理阶段主要处理 #include #define这样的源文件。处理所有的条件编译比如: #if、#ifedef、#elif、#endif等等
  6. 删除所有的#define,并且展开所有的宏定义,用完就会删,通过预处理阶段,MIN被替换,看下面例子,右下角的截图来自test.c的文件
  7. 会删除所有的注释,真正运行代码的时候一行注释都没有,注释是给程序员看的
  8. 假如有头文件包含头文件,会递归的形式展开。还会添加行号文件名标识,方便后面编译器生成调试信息
  9. 保留所有的#pragma的编译器命令,编译器后续会用,
    1. 就好比#pragma pack(),这个可以修改结构体的默认对齐大小
    2. 还有一个#pragma once ,这个是防止头文件重复包含
    3. 下面的例子就很好的说明了问题,#pragma定义确实会保留

2.2编译 test.s 汇编代码

  1. 编译过程是预处理之后的,有三个步骤:词法分析,语法分析,语义分析生成对应的汇编代码文件
  2. gcc -S test.i -o test.s
    1. -S 对中间文件处理,可以放在test.i前后都没问题,最后生成test.s文件的汇编代码
    2. 把C语言代码生成了汇编代码,大家可以去gcc的环境下试试哈

2.2.1三个步骤

1. 词法分析器
  1. 扫描器,就是简单的进行词法分析,把代码分割成不同的记号 (关键字、标识符、字面量、特殊字符等 ),就是把这个一句话拆分成成很多部分,括号,字符,数字,符号,关键字等

2. 语法分析器

  1.  = 对应赋值,+ 加法表达式 ,* 乘法表达式  arr 标识符等等
  2. 可以产生语法书,表达方式是为节点的树。根据表达式的关系构成语法书

3. 语义分析器

    1. 对表达式的语法层面分析,分析的是语义的静态分析,大概的分析语义,并没有执行。
    2. 如果有语法错误会提前报错,就是好比有时候写代码写着写着突然就会有个红色小波浪。
    3. 这个阶段可以知道每个表达式的类型是什么,当然这个过程很复杂的,相当于又是一个专业细分,我们了解一下就ok啦
    4. 大家应该还记得,sizeof 操作符吧,sizeof括号内是不可以计算的,为什么能输出4,都是这个阶段分析出来的

2.3汇编

  1. gcc -c test.s 生成目标文件
  2. 汇编器将汇编代码转换成机器可执行的二进制指令,每个汇编代码都有对应的机器指令
  3. 这个阶段会生成目标文件 test.o test.obj,这种文件是没法打开的里面是二进制

2.4链接

链接解决的是⼀个项目中多文件、多模块之间互相调用的问题。

  1. 把一堆文件链接在一起生成可执行的目标文件 .exe
  2. 每个.c 文件之间都有对应的符号表,符号有对应的地址,把他们整合起来,两个.c文件都有这个地址,但是只能有一个地址,编译器会自己选择有效的地址
  3. 所以调用的地方和声明的地方要符号统一才能找到
  4. 看报错,没办法解析外部符号,所以符号要一样才行
  5. 我们知道,在预处理的时候后有多个.c 文件一起执行,经过编译的过程,生成对应的.o和.obj文件等待被链接,编译器需要通过符号表,引用符号来找到对应正确的地址。因为在等待链接的过程生成了多个地址,编译器需要将相同与之对应符号修正过来成为真正可用的地址。这个过程叫做:重定位

3.运行环境

  1. 程序必须载入内存中,一般都是系统完成。在独立环境里面,程序的载入需要手工安排。还可以通过可执行代码置入只读内存来完成
  2. 程序开始就会调用main函数
  3. 执行代码程序的时候,程序会使用一个运行时间的堆栈(stack),存放局部变量和返回地址,程序也可以同时使用静态(static)内存,存在静态内存里面的会一直保存,直到程序结束
  4. 终止程序,正常终止main函数,可能半路意外终止

总结:

  1. 如果本篇文章对你有帮助,还请留下一键三连,我会继续努力创作出更加详细的文章,重点是理解消化这个过程,特别是在写出BUG的时候,你要知道是在哪个阶段出的问题,可以更好的入手解决问题。
  2. 坚持下去认为对的事情~~

  • 21
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值