news 2026/8/21 23:08:41

【C语言学习】编译和链接

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C语言学习】编译和链接

一、翻译环境和运行环境

在ANSI C的任何一种实现中,存在两个不同的环境:
①翻译环境:源代码被转换成可执行的机器指令(二进制指令);
②执行环境:实际执行代码。

二、翻译环境

翻译环境由编译和链接两个大的过程组成,编译又可以分为:预处理(预编译)编译汇编
一个C语言项目可能有多个.c文件一起构建,如何生成可执行程序呢?
①多个.c文件单独经过编译器,编译生成对应的目标文件;
注:在Windows环境下目标文件后缀是.obj,linux环境下目标文件后缀是.o
②多个目标文件和链接库一起经过链接器处理生成最终的可执行程序;
③链接库是指运行时库(它是支持程序运行的基本函数集合)或或者第三方库。

编译、链接具体操作如下:


Linux环境下,gcc编译器编译和链接过程:

2.1 预处理(预编译)

源文件和头文件被处理成.i为后缀的文件。
预处理阶段主要处理那些#开头的预编译指令:
(1)所有的#define删除,并展开所有的宏定义。(比如,#define M 100,程序中所有变量M会被当成100)
(2)处理预编译指令,如#if、#ifdef、#elif、#else、#endif。
(3)处理#include预编译指令,将包含的头文件的内容插入到该预编译指令的位置。这个过程是递归进行,也就是说包含的头文件也可能包含其他文件。
(4)删除所有的注释。
(5)添加行号和文件名标识,方便后续编译器生成调试信息等。
(6)保留#pragma的编译器指令,编译器后续会使用。
经过预处理(预编译)后的.i文件中不再包含宏定义,因为宏已经被展开。并且包含的头文件都被插入到.i文件中,所以当我们无法知道宏定义或者头文件是否包含正确的时候,可以查看预处理后的.i文件来确认。
Linux环境下,gcc编译器命令:gcc -E test.c -o test.i



2.2 编译

这个过程把C语言代码翻译成汇编代码,涉及词法分析语法分析语义分析及优化。
Linux系统,gcc编译器命令:gcc -S test.i -o test.s
假设代码:array[index] = (index + 4) * (2 + 6);

2.2.1 词法分析

将源代码程序输入扫描器,扫描器的任务就是简单进行词法分析,把代码中的字符分割成一系列的记号(关键字、标识符、字面量、特殊字符等)。

记号类型
1array标识符
2[左方括号
3index标识符
4]右方括号
5=赋值
6左圆括号
7index标识符
8+加号
94数字
10)右原括号
11*乘号
12(左圆括号
132数字
14+加号
156数字
16)右圆括号

2.2.2 语法分析

用语法分析器对扫描产生的记号进行语法分析,从而产生语法树。语法树是以表达式为节点的树。

2.2.3 语义分析

由语义分析器来完成语义分析,对表达式的语法层面分析。编译器所能做的分析是语义的静态分析,静态语义分析通常包括声明和类型的匹配,类型的转换等。这个阶段会报告错误的语法信息。

2.3 汇编

汇编是将汇编代码转变成机器可执行的指令(二进制指令),每一个汇编语句几乎都对应一条机器指令,就是根据汇编指令和机器指令的对照表一一进行翻译,也不做指令优化。
Linux环境下,gcc编译器汇编指令:gcc -c test.s -o test.o;

2.4 链接

链接是一个复杂的过程,链接的时候需要把一堆文件链接在一起才能生成可执行程序。链接过程主要包括:地址和空间分配符号决议重定位等这些步骤。
链接解决的是一个项目中多文件、多模块之间互相调用的问题。
什么是重定位?
例如:在一个C语言项目中有2个.c文件(test.c和add.c)。


两个.c文件中都有g_val和Add,每个.c文件都会为它们分配内存空间。
在test.c文件中每使用一次Add函数和g_val的时候必须确切的知道Add和g_val的地址,但是由于每个文件是单独编译,在编译test.c的时候并不知道Add函数和g_val变量的地址,所以暂时把调用Add的指令的目标地址和g_val的地址搁置。等待最后链接的时候由连接器根据引用的符号Add在其他模块中查找Add函数的地址,然后将test.c中所引用到Add的指令重新修正,让他们的目标地址为真正的Add函数的地址,对于全局变量g_val也是类似的方法来修正地址。这个地址修正的过程叫做:重定位。

三、运行环境

(1)程序必须载入内存。在有操作系统的环境中,一般这个由操作系统完成。在独立的环境中,程序的载入必须由手工安排,也可能是通过可执行代码置入只读内存来完成。
(2)程序的执行便开始。接着调用main函数。
(3)开始执行程序代码。这个时候程序将使用一个运行时堆栈(stack),储存函数的局部变量和返回地址。程序同时也可以使用静态内存,储存于静态内存中的变量在程序整个执行过程一直保留他们的值。
(4)终止程序。正常终止main函数,也可能是意外终止。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/21 23:39:29

ARM64 指令 --- TST/CSEL

文章目录一、TST1.1 TST (immediate)1.2 TST (shifted register)二、CSEL2.1 简介2.2 CSEL的扩展指令2.2.1 CSINC2.2.2 CSINV2.2.3 CSNEG三、demo3.1 tst3.1.1 demo 13.1.2 demo 23.2 TST CSEL3.2.1 demo 13.2.2 demo 23.2.3 总结一、TST 1.1 TST (immediate) TST (immediat…

作者头像 李华
网站建设 2026/8/21 14:12:31

Java全栈开发工程师的面试实战:从基础到微服务

Java全栈开发工程师的面试实战:从基础到微服务 一、开场介绍 面试官:你好,我是今天的面试官,欢迎来到我们公司的技术面试。我看到你的简历上写着你有5年的Java全栈开发经验,主要集中在电商和内容社区方向。可以简单介绍…

作者头像 李华
网站建设 2026/8/21 19:52:31

虚拟网络环境及socket概述

虚拟网络环境及socket概述 文章目录虚拟网络环境及socket概述一、前言二、VMware虚拟机网络环境概述2.1 概述2.2 注意事项2.3 几种组网方式的区别2.4 Vmware和Windows的数据互通三、Socket简介3.1 什么是socket四、socket下的常用编程接口五、小结一、前言 今天,将带…

作者头像 李华
网站建设 2026/8/21 9:23:02

No module named ‘kaldi_native_fbank‘

No module named kaldi_native_fbank解决方法:pip install kaldi-native-fbankpython -c"import kaldi_native_fbank as knf"

作者头像 李华
网站建设 2026/8/20 1:26:52

A47双麦模组:全场景语音清晰革命

在智能对讲时被回音干扰,在车间通话时被机器噪音淹没,在远程教学时因杂音错过重点——这些语音传输的痛点,正在被A47双麦阵列回音消除及降噪模组(V2.0)彻底解决。作为一款聚焦“全场景语音优化”的高性能数字语音处理模…

作者头像 李华