当前位置: 首页 > news >正文

C语言从入门到实战——编译和链接

编译和链接

  • 前言
  • 一、 翻译环境和运行环境
  • 二、 翻译环境
    • 2.1 预处理(预编译)
    • 2.2 编译
      • 2.2.1 词法分析
      • 2.2.2 语法分析
      • 2.2.3 语义分析
    • 2.3 汇编
    • 2.4 链接
  • 三、 运行环境


前言

在C语言中,编译和链接是将源代码转换为可执行文件的两个主要步骤。

编译过程包括以下步骤:

  1. 预处理:将源代码中的预处理指令(如#include#define)替换为实际的代码。
  2. 编译:将预处理后的代码转换为汇编语言。
  3. 汇编:将汇编语言转换为机器码指令。

链接过程包括以下步骤:

  1. 目标文件生成:将每个源文件编译后生成的目标文件(.o.obj)进行合并,生成一个总的目标文件。
  2. 符号解析:查找并解析目标文件中的所有符号(例如全局变量和函数名),以确保每个符号都有一个唯一的地址。
  3. 地址重定位:根据符号表中符号的地址信息,将目标文件中的所有地址引用进行重定位,使它们指向正确的地址。
  4. 符号解析和重定位完成后,将链接后的目标文件与标准库和其他库文件进行链接,生成最终的可执行文件。

在编译和链接过程中,可以使用不同的编译器和链接器来完成这些步骤。常见的C语言编译器包括GCCClangMSVC等,而常见的链接器包括GNU ldMicrosoft Linker等。

编译器和链接器的具体命令和选项可以根据不同的平台和编译环境而有所不同,可以通过编译器和链接器的帮助文档或命令行参数来了解更多信息。


一、 翻译环境和运行环境

ANSIC是一种计算机编程语言,全称为American National Standard Institute C。它是C语言的一个标准化版本,于1989年被美国国家标准学会(ANSI)采纳为正式的国家标准。ANSIC在C语言的基础上进行了一些改进和扩展,增加了一些新的特性和功能,提高了代码的可读性和可靠性。ANSIC的标准化使得不同厂商的编译器能够遵循相同的规范,从而实现了C语言的跨平台性和移植性。

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

第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。

第2种是执行环境,它用于实际执行代码。
在这里插入图片描述

二、 翻译环境

那翻译环境是怎么将源代码转换为可执行的机器指令的呢?这里我们就得展开讲解一下翻译环境所做的事情。

其实翻译环境是由编译和链接两个大的过程组成的,而编译又可以分解成:预处理(有些书也叫预编译)、编译、汇编三个过程。
在这里插入图片描述
一个C语言的项目中可能有多个 .c 文件一起构建,那多个 .c 文件如何生成可执行程序呢?

  • 多个.c文件单独经过编译出编译处理生产对应的目标文件。
  • 注:在Windows环境下的目标文件的后缀是 .objLinux环境下目标文件的后缀是 .o
  • 多个目标文件和链接库一起经过链接器处理生成最终的可执行程序。
  • 链接库是指运行时库(它是支持程序运行的基本函数集合)或者第三方库。

如果再把编译器展开成3个过程,那就变成了下面的过程:
在这里插入图片描述

2.1 预处理(预编译)

在预处理阶段,源文件和头文件会被处理成为.i为后缀的文件。

gcc 环境下想观察一下,对 test.c 文件预处理后的.i文件,命令如下:

gcc -E test.c -o test.i

预处理阶段主要处理那些源文件中#开始的预编译指令。

比如:#include,#define,处理的规则如下:

  • 将所有的 #define 删除,并展开所有的宏定义。
  • 处理所有的条件编译指令,如: #if#ifdef#elif#else#endif
  • 处理#include 预编译指令,将包含的头文件的内容插入到该预编译指令的位置。

这个过程是递归进行的,也就是说被包含的头文件也可能包含其他文件。

  • 删除所有的注释
  • 添加行号和文件名标识,方便后续编译器生成调试信息等。
  • 或保留所有的#pragma的编译器指令,编译器后续会使用。

经过预处理后的.i文件中不再包含宏定义,因为宏已经被展开。并且包含的头文件都被插入到.i入件中。所以当我们无法知道宏定义或者头文件是否包含正确的时候,可以查看预处理后的.i文件来确认。

2.2 编译

编译过程就是将预处理后的文件进行一系列的:词法分析、语法分析、语义分析及优化,生成相应的汇编代码文件。

编译过程的命令如下:

gcc -S test.i -o test.s 

对下面代码进行编译的时候,会怎么做呢?

假设有下面的代码

array[index] = (index+4)*(2+6); 

2.2.1 词法分析

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

上面程序进行词法分析后得到了16个记号:

记号类型
array标识符
[左方括号
index标识符
]右方括号
=赋值
左圆括号
index标识符
+加号
4数字
右圆括号
2数字
+加号
6数字
右圆括号

2.2.2 语法分析

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

在这里插入图片描述

2.2.3 语义分析

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

在这里插入图片描述

2.3 汇编

汇编器是将汇编代码转转变成机器可执行的指令,每一个汇编语句几乎都对应一条机器指令。就是根据汇编指令和机器指令的对照表一一的进行翻译,也不做指令优化。

汇编的命令如下:

gcc -c test.s -o test.o 

2.4 链接

链接是一个复杂的过程,链接的时候需要把一堆文件链接在一起才生成可执行程序。

链接过程主要包括:地址和空间分配,符号决议和重定位等这些步骤。

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

比如:在一个C的项目中有2个.c文件( test.cadd.c ),代码如下:
在这里插入图片描述
test.c

#include <stdio.h>
//test.c
//声明外部函数
extern int Add(int x, int y);
//声明外部的全局变量
extern int g_val;
int main()
{int a = 10;int b = 20;int sum = Add(a, b);printf("%d\n", sum);return 0;
}

add.c

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

我们已经知道,每个源文件都是单独经过编译器处理生成对应的目标文件。

test.c 经过编译器处理生成 test.o
add.c 经过编译器处理生成 add.o

我们在 test.c 的文件中使用了 add.c 文件中的 Add 函数和 g_val 变量。

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

前面我们非常简洁的讲解了一个C的程序是如何编译和链接,到最终生成可执行程序的过程,其实很多内部的细节无法展开讲解。比如:目标文件的格式elf,链接底层实现中的空间与地址分配,符号解析和重定位等,如果你有兴趣,可以看《程序的自我修养》一书来详细了解。

在这里插入图片描述

三、 运行环境

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

相关文章:

C语言从入门到实战——编译和链接

编译和链接 前言一、 翻译环境和运行环境二、 翻译环境2.1 预处理&#xff08;预编译&#xff09;2.2 编译2.2.1 词法分析2.2.2 语法分析2.2.3 语义分析 2.3 汇编2.4 链接 三、 运行环境 前言 在C语言中&#xff0c;编译和链接是将源代码转换为可执行文件的两个主要步骤。 编…...

【实战教程】ThinkPHP6分页功能轻松实现,让你的网站更高效!

ThinkPHP是一款非常流行的PHP开发框架&#xff0c;其最新版本ThinkPHP6在性能和易用性方面都得到了很大的改善。分页功能是网页开发中非常常见的功能&#xff0c;而ThinkPHP6也提供了非常方便的分页方法。本文将介绍如何实现ThinkPHP6的分页功能。 一、了解分页功能 在Web应用…...

专业130+总分380+哈尔滨工程大学810信号与系统考研经验水声电子信息与通信

今年专业课810信号与系统130&#xff0c;总分380顺利考上哈尔滨工程大学&#xff0c;一年的努力终于换来最后的录取&#xff0c;期中复习有得有失&#xff0c;以下总结一下自己的复习经历&#xff0c;希望对大家有帮助&#xff0c;天道酬勤&#xff0c;加油&#xff01;专业课&…...

旅游项目day08

1. 旅游日记&#xff08;游记&#xff09; 后端&#xff1a;实体类&#xff0c;列表&#xff0c;查看&#xff0c;审核 前端&#xff1a;目的地明细中-游记->带范围条件查询&#xff0c;游记首页&#xff0c;【扩展】游记添加/编辑&#xff0c;【扩展】添加游记时间没登录时…...

蓝桥杯真题(Python)每日练Day2

题目 题目分析 对于本题首先确定其数据结构为优先队列&#xff0c;即邮费最小的衣服优先寄&#xff0c;算法符合贪心算法。可以直接使用queue库的PriorityQueue方法实现优先队列。关于PriorityQueue的使用方法主要有&#xff1a; import queue q queue.Queue()# 队列 pq qu…...

IntelliJ IDEA 拉取gitlab项目

一、准备好Gitlab服务器及项目 http://192.168.31.104/root/com.saas.swaggerdemogit 二、打开 IntelliJ IDEA安装插件 打开GitLab上的项目&#xff0c;输入项目地址 http://192.168.31.104/root/com.saas.swaggerdemogit 弹出输入登录用户名密码&#xff0c;完成。 操作Comm…...

RHCSA上课笔记(前半部分)

第一部分 网络服务 第一章 例行性工作 1.单一执行的例行性工作 单一执行的例行性工作&#xff08;就像某一个时间点 的闹钟&#xff09;&#xff1a;仅处理执行一次 1.1 at命令&#xff1a;定时任务信息 [rhellocalhost ~]$ rpm -qa |grep -w at at-spi2-core-2.40.3-1.el9.x…...

C++代码入门05 字符串容器

图源&#xff1a;文心一言 上机题目练习整理&#xff0c;本篇作为字符串容器的代码&#xff0c;提供了常规解法及其详细解释&#xff0c;供小伙伴们参考~&#x1f95d;&#x1f95d; 第1版&#xff1a;在力扣新手村刷题的记录~&#x1f9e9;&#x1f9e9; 方法&#xff1a;常…...

vue3 项目中 arguments 对象获取失败问题

问题 在 vue3 项目中 获取到的 arguments 对象与传入实参不符&#xff0c;打印出函数中的 arguments 对象显示如下&#xff1a; 原因 作者仔细回看代码才发现&#xff0c;自己一直用的是 vue3 的组合式写法&#xff0c;函数都是箭头函数&#xff0c;而箭头函数不存在 argumen…...

12.线程同步

12.线程同步 1. 为什么需要线程同步2. 互斥锁2.1 互斥锁初始化2.1.1 PTHREAD_MUTEX_INITIALIZER 宏初始化2.1.2 使用函数初始化 2.2 加锁和解锁2.3 pthread_mutex_trylock()2.4 销毁互斥锁2.5 互斥锁死锁2.6 互斥锁的属性 3. 条件变量3.1 条件变量初始化3.2 通知和等待条件变量…...

开发安全之:System Information Leak: External

Overview 在调用 error_reporting() 过程中&#xff0c;程序可能会显示系统数据或调试信息。由 error_reporting() 揭示的信息有助于攻击者制定攻击计划。 Details 当系统数据或调试信息通过套接字或网络连接使程序流向远程机器时&#xff0c;就会发生外部信息泄露。 示例 1…...

burp靶场--文件上传

burp靶场–文件上传 https://portswigger.net/web-security/file-upload/lab-file-upload-remote-code-execution-via-web-shell-upload 1.文件上传 1、原理&#xff1a;文件上传漏洞是指Web服务器允许用户将文件上传到其文件系统&#xff0c;而不充分验证文件的名称、类型、…...

mac 中vscode设置root启动

1. 找到你的vscode app&#xff0c;点击鼠标右键------->选项----->在访达中显示 2. 终端中输入以下命令&#xff0c;不要点回车&#xff0c;不要点回车&#xff0c;输入一个空格 sudo chflags uchg 3. 然后将你的程序拖到终端&#xff0c;会自动…...

【MySQL数据库专项 一】一个例子讲清楚数据库三范式

好的&#xff0c;让我们以学校数据库中的一个表为例来说明第一范式&#xff08;1NF&#xff09;、第二范式&#xff08;2NF&#xff09;和第三范式&#xff08;3NF&#xff09;的概念。 什么是数据库三范式 数据库的范式&#xff08;Normalization&#xff09;是一组关于数据…...

【笔记】关于期刊

什么是统计源期刊 统计源期刊&#xff0c;全称为“中国科技论文统计源期刊”&#xff0c;也称作中国科技核心期刊&#xff0c;是由中国科技信息研究所&#xff08;ISTIC&#xff09;受国家科技部委托&#xff0c;选定的一系列在中国出版的高质量自然科学类学术期刊。这些期刊是…...

SpringMVC-.xml的配置

文章目录 一、对pom.xml的配置二、对web.xml1.第一种方式2. 第二种方式 三、对SpringMVC.xml的配置 一、对pom.xml的配置 <!-- 打包成war包--><packaging>war</packaging> <dependencies><!-- SpringMVC--><dependency><gro…...

Java找二叉树的公共祖先

描述&#xff1a; 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节…...

《Linux高性能服务器编程》笔记03

Linux高性能服务器编程 本文是读书笔记&#xff0c;如有侵权&#xff0c;请联系删除。 参考 Linux高性能服务器编程源码: https://github.com/raichen/LinuxServerCodes 豆瓣: Linux高性能服务器编程 文章目录 Linux高性能服务器编程第07章 Linux服务器程序规范7.1日志7.2用…...

Java毕业设计-基于ssm的网上求职招聘管理系统-第85期

获取源码资料&#xff0c;请移步从戎源码网&#xff1a;从戎源码网_专业的计算机毕业设计网站 项目介绍 基于ssm的网上求职招聘管理系统&#xff1a;前端 jsp、jquery&#xff0c;后端 springmvc、spring、mybatis&#xff0c;角色分为管理员、招聘人员、用户&#xff1b;集成…...

UDP和TCP

UDP协议是一种不可靠的、面向无连接的协议。在通信过程中&#xff0c;它并不像TCP那样需要先建立一个连接&#xff0c;只要&#xff08;目的地址&#xff0c;端口号&#xff0c;源地址&#xff0c;端口号&#xff09;确定了&#xff0c;就可以直接发送信息报文&#xff0c;并且…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

多场景 OkHttpClient 管理器 - Android 网络通信解决方案

下面是一个完整的 Android 实现&#xff0c;展示如何创建和管理多个 OkHttpClient 实例&#xff0c;分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

pam_env.so模块配置解析

在PAM&#xff08;Pluggable Authentication Modules&#xff09;配置中&#xff0c; /etc/pam.d/su 文件相关配置含义如下&#xff1a; 配置解析 auth required pam_env.so1. 字段分解 字段值说明模块类型auth认证类模块&#xff0c;负责验证用户身份&am…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...