C语言程序运行需要的两大环境《C语言进阶》

目录

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

翻译环境分为两部分,编译+链接

第一步:预编译(预处理)

第二步,编译

第三步:汇编

关于运行环境分为四点:

关于链接库


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

在 ANSI C(标准C) 的任何一种实现中,存在两个不同的环境。
*第1种是翻译环境。 在这个环境中源代码被转换为可执行的机器指令。
在编译器中(以VS2019为例),我们写的C语言代码都是文本的信息(各种字符串,数据,结构体等),站在人类的角度,我们能够理解,但是计算机不行,代码需要翻译成计算机呢能够识别的指令:二进制指令


***第2种是执行环境。**它用于实际执行代码。
代码经过翻译环境后生成的二进制指令代码,由执行环境来执行生成。

代码编译时生成可执行程序时的步骤:

1. 组成一个程序的每个源文件通过编译过程分别转换成目标代码( object code )。
2. 每个目标文件由链接器( linker )捆绑在一起,形成一个单一而完整的可执行程序。
3. 链接器同时也会引入标准 C 函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人的程序库,将其需要的函数也链接到程序中。
简述来说:
每一个源文件(.c),都单独经过编译器进行编译,生成目标文件(.obj),目标文件和链接库再通过链接器的处理(这个过程就叫链接),就生成了可执行程序(.exe)。
如下图

翻译环境分为两部分,编译+链接

编译本身也分为几个阶段:预处理 , 编译 ,汇编。

接下来我们用一个简单的代码,源文件名(test.c),使用gcc这个编辑器,给大家演示整个过程;

第一步:预编译(预处理)

预编译的指令是:gcc 源文件.c -E - 源文件名 + .i

  1. 预处理 选项 gcc - E test.c - o test.i
    预处理完成之后就停下来,预处理之后产生的结果都放在 test.i文件中,具体内容可看下图。

如下面的代码:

cpp 复制代码
源文件文件名 :test.c

#include<stdio.h>

//定义全局变量,赋值2023
int year = 2023;
#define M 100
int main()
{
	int a = M;
	printf("%d\n", M);

	return 0;
}

1.输入指令gcc test.c -E -test i

终端输出:


2.观察test.c和test .i的内容


3.在test.i文件中查看

我们发现,在test.i整个代码的末尾,才是我们源代码的内容,那前面的几百行代码又是什么呢,是头文件<stdio.h>的整个内容包含进来了。同时细心的同学发现,test.i中没有了注释,也没有了宏定义的符号M了,所以预处理的作用是:

  1. 注释的删除
  2. #include<stddio.h>头文件的包含
  3. #define 符号的替换
  4. 文本操作
  5. 所以的预处理指令都是在预处理阶段处理的

第二步,编译

编译指令:gcc -S 源文件.c / gcc -S 源文件名 + .i

编译 选项 gcc - S test.c
编译 选项 gcc -S test.i (编译时,这两个文件都可以输入指令中,都会生成文件test.s)
编译完成之后就停下来,结果保存在 test.s 中。
我们输入 gcc - S test.c 为例

1.输入指令gcc -S test.c / gcc -S test.i

终端输出:


2.生成了test.s文件的内容并展示test.s文件的内容


3.总结:

上面黄圈部分就是test.s的内容,里面就是汇编指令。编译的作用是:把c语言代码翻译成汇编指令 。编译的方式是通过:语法分析,词法分析,语义分析,符号汇总等方式。总的来说,编译就是把我们的c语言代码拆解分析,然后翻译成汇编指令给下一步汇编的动作。

第三步:汇编

汇编指令:gcc -c 源文件.c / gcc -c 源文件名 + .s

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

1.输入指令gcc -c test.c / gcc-c test.s

终端输出:


2.生成了test.o的目标文件

但是你会发现这是test.o目标文件存放的二进制文件,编译器是不支持显示的的,如果仍要打开,如下图;


  1. 总结:

1.目标文件中存放的是二进制的指令

2.汇编是把汇编指令翻译成二进制指令

顺便提一下:Linux下gcc编译产生的目标文件test.o,可执行程序test都是按照ELF的这种文件格式存储的

最后,以上过程完成了编译,之后到链接过程

链接指令:gcc 源文件名.o -o 新文件名

对目标文件进行链接。可生成一个可执行文件

1.输入指令:gcc test.o -o vskkk

输出终端:


2,对vskkk的内容进行查看

但是文件还是二进制的指令无法查看。


3.总结:

链接的作用是:

1.合并线段

2.符号表的合并和重定位


关于运行环境分为四点:

运行环境相较于翻译环境就好理解的多了,通俗理解有以下几点:

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

关于链接库

我们知道再写C语言代码时,只要加上头文件 #include<stdio.h> ,就可以直接使用库函数了,比如scanf(),printf(),那为什么可以直接使用呢?原因是我们把这些库函数都已经提前编译好,打包好放在静态库中,可以直接提供给我们,比如我们熟悉的scanf函数,如下图的打红圈内容,标记部分后缀以LIB结尾的,就是静态库(.LIB),库函数的静态库和目标文件在链接器中发生链接生成可执行程序。程序就可以运行啦。


以上就是全部内容了,希望能帮助到大家,如果可以,也希望大家给博主点点赞支持一下,谢谢。如果有错误的地方,希望大家能在评论区批评指出,我会进行订正的。

相关推荐
ahadee9 分钟前
蓝桥杯每日真题 - 第12天
c++·vscode·算法·蓝桥杯
zhentiya22 分钟前
微积分第五版课后习题答案详解PDF电子版 赵树嫄
算法·pdf
luky!1 小时前
算法--解决熄灯问题
python·算法
鸽鸽程序猿1 小时前
【算法】【优选算法】二分查找算法(下)
java·算法·二分查找算法
_OLi_1 小时前
力扣 LeetCode 150. 逆波兰表达式求值(Day5:栈与队列)
算法·leetcode·职场和发展
远望清一色1 小时前
基于MATLAB身份证号码识别
开发语言·图像处理·算法·matlab
醉颜凉2 小时前
【NOIP提高组】潜伏者
java·c语言·开发语言·c++·算法
lapiii3582 小时前
图论-代码随想录刷题记录[JAVA]
java·数据结构·算法·图论
Dontla4 小时前
Rust泛型系统类型推导原理(Rust类型推导、泛型类型推导、泛型推导)为什么在某些情况必须手动添加泛型特征约束?(泛型trait约束)
开发语言·算法·rust