C语言程序环境剖析——探究从.c到.exe之路

2023-11-19

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

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

  1. 翻译环境,在这个环境中源代码被转换成可执行的机器指令。
    (例如:vs2022这个集成开发环境就包括翻译环境)
  2. 执行环境, 它用于实际执行代码

2. 详解编译+ 链接

一个.c文件想要编程一个可执行程序,需要经过编译链接两步, 而编译又可细分为预编译编译汇编三过程,如图:
在这里插入图片描述

2.1 翻译环境

一个项目可能是由很多个.c文件共同构成的,每个.c文件需要分别进行编译产生.obj后缀的目标文件(windows环境)

在linux环境下是.o为后缀

接着在链接器的作用下将所有目标文件结合在一起进行最后产生了可执行程序。
在这里插入图片描述

  • 组成一个程序的每个源文件单独通过编译过程分别转换成目标代码(object code)
  • 每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。
  • 链接器同时会引入标准C函数库中任何被程序引用到的函数,而且它可以搜索程序员个人的程序库,将其所需要的函数也链接到程序中。

2.2 编译的三部分

下面,我们就以一个例子来为大家简单的解释一下每个步骤都在干什么。

注:以下例子只是用来演示c语言编译各个阶段的特点,无实际意义

test.c

#include<stdio.h>
#include "add.c"
//这是注释
#define M 100
extern int g_val;
extern int Add(int x, int y);
int main()
{
    //开始
    struct s k;
    int b = 10;
    int c = Add(M,b);
    printf("%d", c);
    return 0;
    //结束
}

add.c

int Add(int x, int y)
{
    return x + y;
}

struct s
{
    int a;
    char c;
};

int g_val = 100;
预编译

在预编译过程中,会进行的操作有#include头文件的包含以及#define 宏的替换(预处理指令)还有注释的删除,总的来说,就是对代码文本进行改进。

我们用gcc编译器来编译代码,通过指令使其停在预处理结束之后的阶段,由此来观看代码文本的变化。

在终端输入: gcc -E test.c -o test.i即可生成预处理之后的文件,然后我们看看文件中的内容,如图:
在这里插入图片描述

编译

编译过程主要将进行语法分析词法分析语义分析以及符号汇总四个内容,同样,我们可以通过命令的方式得到编译后的文件。

这里简单讲解一下符号汇总,其实就是把函数名和全局变量之类的汇总起来,便于下一步作用。

命令:gcc -S test.c,然后我们看一下文件内容。

在这里插入图片描述
我们可以看到,其实经过编译之后,就是把我们的c语言代码转换为汇编代码。

汇编

接下来就是编译的最后一步,汇编了,通过这一步之后,我们写的c程序就完全转换成了目标文件(object code),由于目标文件是由二进制指令构成,所以我们是没办法看懂的,但是依旧可以通过指令的方式得到汇编后生成的文件。
指令: gcc -C test.c
通过这一指令,将生成test.o的文件,其和我们用vs编译生成的.obj 后缀文件性质相同。
在这里插入图片描述
汇编过程的进行大致有以下两步:

  1. 形成符号表

在编译过程中进行了符号汇总,而形成符号表简单来说就是将这些符号与地址配对,符号表 = 符号+地址
例如上面那个例子,两个文件的符号表就可以如此表示:
(地址是博主随便设的(doge))
test.c的符号表

符号 地址
g_val 0x010
Add 0x783
main 0x666

add.c的符号表

符号 地址
Add 0x200
g_val 0x300
  1. 汇编指令-> 二进制指令 ------->test.o和add.o

2.3链接

链接部分主要执行两个操作:

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

这里为了解释合并段表,我们进行一定的扩展:

在linux环境下,生成的test.o和可执行程序都是elf格式的文件,而elf格式的文件时分段式的结构,每个段放置不同功能的数据,如图:
在这里插入图片描述
也就是说,这里段表的合并就是把多个.o文件中相同功能的段合并在一起。

而符号表的合并和重定位就是将相同的符号合并,并且替换掉无效的地址。

就拿刚才的例子来说,test.c 中的g_val 和Add符号是用extern外部引入的,需要在其他的文件才能找到其定义,也就是说,此时test 文件的符号表中这两个的地址是无效的,真正有效的地址在add.c中,因此需要进行合并。

上诉代码合并之后的符号表如下:

符号 地址
g_val 0x300
Add 0x200
main 0x666

以上就是从.c文件到.exe 文件的大体过程啦,当然其实编译的过程还有很多细节,如果有机会,之后博主会继续补充的嘿嘿!


3.运行环境

程序的执行过程:

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

以上就是关于程序环境的相关内容了,如果觉得写的不错还请大家多多点赞啊哈哈!

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言程序环境剖析——探究从.c到.exe之路 的相关文章

  • 在 /dev/input/eventX 中写入事件需要哪些命令?

    我正在开发一个android需要将触摸事件发送到 dev input eventX 的应用程序 我知道C执行此类操作的代码结构如下 struct input event struct timeval time unsigned short
  • GCC 和 ld 找不到导出的符号...但它们在那里

    我有一个 C 库和一个 C 应用程序 尝试使用从该库导出的函数和类 该库构建良好 应用程序可以编译 但无法链接 我得到的错误遵循以下形式 app source file cpp text 0x2fdb 对 lib namespace Get
  • PHP 致命错误:未找到“MongoClient”类

    我有一个使用 Apache 的网站 代码如下 当我尝试访问它时 我在 error log 中收到错误 PHP Fatal Error Class MongoClient not found 以下是可能错误的设置 但我认为没有错误 php i
  • 如何模拟ARM处理器运行环境并加载Linux内核模块?

    我尝试加载我的vmlinux into gdb并使用 ARM 内核模拟器 但我不明白为什么我会得到Undefined target command sim 这是外壳输出 arm eabi gdb vmlinux GNU gdb GDB 7
  • 为什么我收到的数据包数据大小大于mss?

    我在两台 PC 上使用 ifconfig ethX mtu 300 修改了 MTU 并使用 netperf 测试网络 我用 WireShark 嗅探了 SYN 数据包中的 MSS 260 但我得到了一些大于 260 的数据包 为什么 嗅探器
  • 拆分字符串以仅获取前 5 个字符

    我想去那个地点 var log src ap kernelmodule 10 001 100 但看起来我的代码必须处理 ap kernelmodule 10 002 100 ap kernelmodule 10 003 101 等 我想使用
  • Unix 命令列出包含字符串但*不*包含另一个字符串的文件

    如何递归查看包含一个字符串且不包含另一个字符串的文件列表 另外 我的意思是评估文件的文本 而不是文件名 结论 根据评论 我最终使用了 find name html exec grep lR base maps xargs grep L ba
  • 修改linux下的路径

    虽然我认为我已经接近 Linux 专业人士 但显然我仍然是一个初学者 当我登录服务器时 我需要使用最新版本的R 统计软件 R 安装在 2 个地方 当我运行以下命令时 which R I get usr bin R 进而 R version
  • bluetoothctl 到 hcitool 等效命令

    在 Linux 中 我曾经使用 hidd connect mmac 来连接 BT 设备 但自 Bluez5 以来 这种情况已经消失了 我可以使用 bluetoothctl 手动建立连接 但我需要从我的应用程序使用这些命令 并且使用 blue
  • 从 PL/SQL 调用 shell 脚本,但 shell 以 grid 用户而非 oracle 身份执行

    我正在尝试使用 Runtime getRuntime exec 从 Oracle 数据库内部执行 shell 脚本 在 Red Hat 5 5 上运行的 Oracle 11 2 0 4 EE CREATE OR REPLACE proced
  • awk 子串单个字符

    这是columns txt aaa bbb 3 ccc ddd 2 eee fff 1 3 3 g 3 hhh i jjj 3 kkk ll 3 mm nn oo 3 我可以找到第二列以 b 开头的行 awk if substr 2 1 1
  • 通过特定分隔符删除字符串

    我的文件中有几列 其中第二列有 分隔符 我想删除第二列中的第一个 第三个和第四个字符串 并将第二个字符串留在该列中 但我有正常的分隔符空间 所以我不知道 input 22 16050075 A G 16050075 A G 22 16050
  • nginx 上的多个网站和可用网站

    通过 nginx 的基本安装 您的sites available文件夹只有一个文件 default 怎么样sites available文件夹的工作原理以及如何使用它来托管多个 单独的 网站 只是为了添加另一种方法 您可以为您托管的每个虚拟
  • 如何在bash中使用jq从变量中包含的json中提取值

    我正在编写一个 bash 脚本 其中存储了一个 json 值 现在我想使用 Jq 提取该 json 中的值 使用的代码是 json val code lyz1To6ZTWClDHSiaeXyxg redirect to http examp
  • 如何在 shell 脚本中并行运行多个实例以提高时间效率[重复]

    这个问题在这里已经有答案了 我正在使用 shell 脚本 它读取 16000 行的输入文件 运行该脚本需要8个多小时 我需要减少它 所以我将其划分为 8 个实例并读取数据 其中我使用 for 循环迭代 8 个文件 并在其中使用 while
  • 如何查明CONFIG_FANOTIFY_ACCESS_PERMISSIONS是否启用?

    我想利用fanotify 7 http man7 org linux man pages man7 fanotify 7 html我遇到的问题是在某些内核上CONFIG FANOTIFY ACCESS PERMISSIONS不起作用 虽然C
  • 为什么内核需要虚拟寻址?

    在Linux中 每个进程都有其虚拟地址空间 例如 32位系统为4GB 其中3GB为进程保留 1GB为内核保留 这种虚拟寻址机制有助于隔离每个进程的地址空间 对于流程来说这是可以理解的 因为有很多流程 但既然我们只有 1 个内核 那么为什么我
  • Linux中的定时器类

    我需要一个计时器来以相对较低的分辨率执行回调 在 Linux 中实现此类 C 计时器类的最佳方法是什么 有我可以使用的库吗 如果您在框架 Glib Qt Wx 内编写 那么您已经拥有一个具有定时回调功能的事件循环 我认为情况并非如此 如果您
  • 加载数据infile,Windows和Linux的区别

    我有一个需要导入到 MySQL 表的文件 这是我的命令 LOAD DATA LOCAL INFILE C test csv INTO TABLE logs fields terminated by LINES terminated BY n
  • 尝试安装 LESS 时出现“请尝试以 root/管理员身份再次运行此命令”错误

    我正在尝试在我的计算机上安装 LESS 并且已经安装了节点 但是 当我输入 node install g less 时 出现以下错误 并且不知道该怎么办 FPaulMAC bin paul npm install g less npm ER

随机推荐

  • 区块链能提供有效的身份管理?

    随着身份盗窃和数据泄露在世界各地越来越多的情况下 身份验证是一个主要问题 对访问数据的人进行身份验证实际上是他们要求的 每天 数以百万计的人在网上进行不同的活动 从研究一个学术话题 到购买新的项目 到在社交媒体平台上发表评论 甚至进行不同的
  • /etc/init.d

    etc init d目录在Linux系统中可是大名鼎鼎 它只负责一件事情 但却涉及到全系统 它包含系统中各种服务的start stop脚本 从acpid到x11 common 其重要性可见一斑 init d 初始化脚本称之为System V
  • 如何生成序号_合并单元格自动添加序号,还在手动输入就out了,学会三组函数公式一秒搞定...

    相信许多同学在用Excel表格登记各类数据的时候 为了规范表格我们经常会用到序号来进行数据标记 许多朋友在更新序号的时候 基本都是手动输入1 2 3等等 然后手动往下拖动 但是这样数据量比较大的时候 就会比较麻烦 而且如果数据是有合并单元格
  • React-Native使用react-native-community/art实现水波纹、音频波动效果

    效果如下 可以通过改变volume值实现动态效果 贴组件代码 复制就能用 依赖package json react native community art 1 1 2 react native 0 61 4 组件代码DancingLine
  • 第一次竞赛-B.连接竹竿

    B 连接竹竿 Alice从市场上买了N根竹竿 每根竹竿都以 节 为单位 这些竹竿中最短的有A节 最长的有B节 其余竹竿各有长短 每根竿的节数也必定在 A B 范围内 现在Alice希望将这些竹竿用连接部件全部接成一根长竹竿 连接部件的长度忽
  • hive详解——RANK()、DENSE_RANK()、ROW_NUMBER()

    概念 RANK 排序相同时会重复 总数不会变 DENSE RANK 排序相同时会重复 总数会减少 ROW NUMBER 会根据顺序排算 实操讲解 现在有一张score表 做查询操作 SELECT RANK over PARTITION BY
  • 初识C++

    目录 前言 什么是C 1 第一个关键字 namespace 1 namespace的用处 2 如果使用命名空间中的变量和函数 2 C 的输入和输出函数 3 缺省参数 4 函数重载 5 引用 1 初识引用 2 引用的类型 3 引用的特性 4
  • 微信小程序领取查看优惠券,会员卡总结

    又见面了 新项目新需求 这次谈谈小程序微信卡券领卡到查看卡券的功能 在做之前 脑子一头雾水 网上查了资料 基本都是领取卡券的介绍 以为很难实现呢 其实主要工作还是在后台配置以及接口处理 前端的工作量不多 主要就是调取小程序提供的卡券接口 a
  • Gephi入学教程基础记录

    Gephi入学教程基础记录 Gephi版本0 8 1 1 CSV数据输入 1 1 中文显示问题 1 2 标签设置 2 自动生成数据 3 编辑工具介绍 1 节点的移动 2 节点的放大和缩小 3 调整节点颜色 4 边的粗细的调整 6 节点的编辑
  • (可能是)完美解决WSL2重启变IP问题

    WSL2的升级对比WSL1 IO升级是巨大的 以及完整的Linux内核 等等都是完美的Linux发行版 Windows10 解决方法有几步一步一步解决 编辑bat脚本 此方法在 microsoft WSL issues 418 获得 开机启
  • Anlios装grouplist 组件之后报错,安装tiger-vncserver

    因为之前升级了一个epel release源 然后containerd也装进去了 但是版本太低 然后以为是runc挡住了 发现没有runc 删完了containerd就可以装了 rpm ivh http mirrors wlnmp com
  • 用数组实现队列和循环队列

    1 先用数组实现一个队列 package com lv queue import java util Scanner public class ArrayQueueDemo public static void main String ar
  • Unity重要知识点

    脚本生命周期 每当脚本被加载时调用一次 1 在Awake中做一些初始化操作 void Awake 初始化public成员 2 在每次激活脚步时调用 void OnEnable 在第一次调用Update之前调用一次Start 即使取消激活 再
  • 通过注册表永久更改cmd控制台的编码为65001

    1 Win R快捷键打开注册表输入regedit 2 路径填入 计算机 HKEY LOCAL MACHINE SOFTWARE Microsoft Command Processor 3 在右边窗口新建字符串值autorun 4 双击打开a
  • Pytest自动化测试框架

    fixture 特点 命令灵活 对于setup teardown可以省略 数据共享 在conftest py配置里写方法可以实现数据共享 不需要import导入 可以跨文件共享 scope的层次及神奇的yield组合相当于各种setup和t
  • win10 ping不通 Docker ip(解决截图)

    背景 win10下载了docker desktop就是这个图 然后计划做一个springboot连接docker docker部署springboot docker 部署springboot 成功 截图 總鑽風的博客 CSDN博客 问题 s
  • 区块链技术及应用概述

    一 基本概念 什么是区块链 区块链是一种以密码学方式保证的不可篡改和不可伪造的分布式账本 关键特点 去中心化 不可篡改性 匿名性 安全可信 区块链架构 1 数据层 主要描述区块链系统的物理形式 它是从Genesis区块开始的区块链链结构 包
  • Java学习笔记11

    API与String 什么是API String String的构造方法 String对象的特点 字符串的比较 获取字符串的字符 什么是API api是application programming interface 应用程序编程接口 S
  • AD20 如何更改标题栏?Altium Designer20 实用技巧系列教程(二)

    AD20 如何更改标题栏 Altium Designer20 实用技巧系列教程 二 视频地址 https www bilibili com video BV1kf4y117TH
  • C语言程序环境剖析——探究从.c到.exe之路

    程序环境 1 程序的翻译环境和执行环境 2 详解编译 链接 2 1 翻译环境 2 2 编译的三部分 预编译 编译 汇编 2 3链接 3 运行环境 1 程序的翻译环境和执行环境 在ANSI C的任何一种实现中 都存在两个不同的环境 翻译环境