C语言编译和链接

翻译环境和运行环境

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

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

.第二种是执行环境,它用于实际执行代码

翻译环境

翻译环境是由编译和链接两个大过程组成,而编译又可以分解成:预处理(预编译)、编译和汇编三个过程

.多个c文件单独经过编译处理产生对应的目标文件

.在Windows环境下目标文件的后缀.obj,Linux环境下目标文件下的后缀是.o

.多个目标文件和链接库一起经过链接器处理生成最终的可执行程序

.链接库是指运行时库(它是支持运行的基本函数集合)或者第三方库 

运行环境 

程序执行的过程:

① 一般来说,程先是被操作系统载入系统中。在独立的系统中,程序的载入叶可能是通过可执行代码置入只读内存来完成。

② 程序运行开始,接着便调用main函数。

③ 操作系统开始执行程序代码。这个时候程序将使用一个运行时堆栈,用来存储函数的局部变量和返回地址。程序同时也可以使用静态内存,存储与静态内存中的变量在程序的整个执行过程中一直保留它们的值。

④ 终止程序。操作系统正常终止main函数,也有可能是意外终止的。

1.预处理(预编译)

主要用来进行一些文本操作

将源文件和头文件处理成.i为后缀的文件
处理命令:gcc -E test.c -o test.i                                (gcc环境下)

在这个阶段主要处理源文件中一些预编译指令:#include        #define

处理规则:

.将所有的#define删除,并展开所有的宏定义

.处理所有的条件编译指令,如:#if、#ifdef、#elif 、#else、#endif

.处理#include预编译指令,将包含的头文件的内容插入到该预编译指令的位置。这个过程是递归进行的,也就是说包含的头文件也可能包含其他文件

.删除所有的注释

.添加行和文件标识,方便后续编译器生成调试信息等

.或保留所有的#pragma的编译器指令,编译器后续会使用

 2.编译

编译就是将C语言代码翻译成汇编代码的过程,经过词法分析、语法分析、语义分析及优化

编译指令:gcc -S test.i -o test.s

3.汇编

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

gcc -c test.s -o test.o 

4.链接

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

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

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

一.预定义符号

1.__FILE__        //进行编译的源文件

2.__LINE__        //文件当前的行号

3.__DATE__       //文件被编译的日期

4.__TIME__        //文件被编译的时间

5.__STDC__        //如果编译器遵循ANSI C,其值为一,否则未定义

二.#define 

1.#define定义常量

#define name stuff                       

例子:#define N   10(末尾不加;) 

2.#define定义宏

#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)

#define name (parament_list) stuff          //parament_list是一个符号隔开的符号表,它们可能出现在stuff中

 例子:#define SQUARE(x)      x*x

但是定义宏存在一个问题:

#define N      5

……

printf("%d\n",SQUARE(N+1));

上面代码输出的是11而不是36,这是为什么?其实定义宏只是简单的对数据进行直接替换并不会进行运算,所以变成5+1*5+1=11。宏的这种特性就会使操作符的优先级会影响宏的值,所以必要的括号需要添加,如SQUARE(x)      (x)*(x)就能避免上述的情况。  

2.1.宏替换的规则

1.在调用宏,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。

2.替换文本随后被插入到程序中原来的文本位置。对于宏,参数名被它们的值所替换。

3.最后,再次对结果文本进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。

注意:

1.宏参数和#define定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归

2.当处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索

2.2.宏函数的对比 

1.实际在执行小型计算工作时,宏比函数在程序规模和速度方面更胜一筹

2.其次,函数的参数必须声明其特定的类型,而宏的参数是无类型的

与函数相比的劣势:

1.每次使用宏的时候,一份宏的定义的代码将插入到程序中。除非宏比较短,否则可能大幅度增加程序的长度

2.宏是没办法进行调试的,也没办法递归

3.宏由于与类型无关,也就不够严谨

4.上面讲到操作符的优先级可能会影响宏的值,导致程序出错

2.3.#undef

用于移除一个宏定义

#undef  name//在该语句下面所移除的宏将不能再被使用

三.#和##

1.#运算符

它是将宏的一个参数转换为字符串字面量,它仅允许出现在带参数的宏替换列表中

代码演示:int n=10;

#define        PRINT(n)     printf("#n=%d",n)        //n=10(第一个n不会转换为数值)

2.##运算符 

它可以将它两边的符号合并为一个符号

#define     M(str1,str2)  str1##str2

……

printf(“%s\n”,M(stu dent));//预处理后宏会被替换成student

四.条件编译

 在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便。因为我们有条件编译指令。

常见的条件编译指令:

1.

#if    常量表达式

                //....

#endif

//常量表达式由预处理器求值(如用#define定义                 #define  __DEBUG__)

2.多分支的条件编译

#if    常量表达式

                //....

#elif  常量表达式

                //....

#else

                //....

#endif

3.判断是否被定义

#if   define(symbol)

#ifdef symbol

 

#if !defined(symbol)

#ifndef   symbol

 

4.嵌套指令

#if defined(OS_UNIX)

                #ifdef  OPTION1

                        unix_version_option1();

                #endif

                #ifdef  OPTION2

                        unix_version_option2();

                #endif

#elif  defined (OS_MSDOS)

                #ifdef  OPTION2

                        msdos_version_option2();

                #endif

#endif

五。头文件的包含

1.头文件被包含的方式:

1.1本地文件包含

#include "game.h"

查找方式:先在源文件所在目录下查找,如果未找到,编译器就像查找库函数头文件一样在标准库位置查找头文件,如果还是找不到就提示编译错误。

1.2库文件的包含

#include<game.h>//这里的头文件只是举例演示,并不是代表真的有这个头文件 

查找方式:直接去标准路径下去查找,如果找不到就提示编译错误。

 2.嵌套文件的包含

如果在一个程序中一个头文件被包含了很多次,那么同时也就会编译多次。这会使编译的压力大大增加。所以有办法可以避免头文件被重复引入呢?这就可以使用我们上文提到的条件编译。

#ifdef __TEST_H__

#define __TEST_H__

        //引入头文件

#endif  

或者也可以使用#pragma once来解决 

以上就是对于C语言编译和链接的内容,篇幅有点长,希望对看到的小伙伴有帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/333453.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

交叉编译工具 aarch64-linux-gnu-gcc 的介绍与安装

AArch64 是随 ARMv8 ISA 一起引入的 64 位架构&#xff0c;用于执行 A64 指令的计算机。而且在 AArch64 状态下执行的代码只能使用 A64 指令集。&#xff0c;而不能执行 A32 或 T32 指令。但是&#xff0c;与 AArch32 中不同&#xff0c;在64位状态下&#xff0c;指令可以访问 …

ArcGIS Pro控件汇总

控件来源 我们对其一一进行查看是否有控件 查看位置 控件展示 ribbonControls 展示 代码 <controls:ProWindow x:Class"ProAppModule9.ProWindowRibbon"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:controls"clr-…

集成学习算法(Bagging 思想、Boosting思想)及具体案例

概述&#xff1a;是机器学习中的一种思想&#xff0c;通过多个模型的组合形成一个精度更高的模型&#xff0c;参与组合的模型称为弱学习器 1、Bagging 思想 有放回的抽样&#xff08;booststrap抽样&#xff09;产生不同的训练集&#xff0c;从而训练不同的学习器&#xff1b;…

FairGuard游戏安全2023年度报告

导 读&#xff1a;2023年&#xff0c;游戏行业摆脱了疫情带来诸多负面影响&#xff0c;国内游戏市场收入与用户规模双双实现突破&#xff0c;迎来了历史新高点。但游戏黑灰产规模也在迅速扩大&#xff0c;不少游戏饱受其侵扰&#xff0c;游戏厂商愈发重视游戏安全问题。 为帮助…

重磅发布!基于百度飞桨的《人工智能基础及应用》书籍正式上线

科技日新月异的今天&#xff0c;人工智能已经成为引领未来的核心驱动力。为了帮助大家更好地深入理解人工智能的理论和技术&#xff0c;为未来发展做好准备&#xff0c;百度飞桨教材编写组联合北京交通大学王方石教授、北京邮电大学杨煜清特聘副研究员共同撰写推出了《人工智能…

使用 FFmpeg 轻松调整视频的大小/缩放/更改分辨率

在此 FFmpeg 教程中&#xff0c;我们学习使用 FFmpeg 的命令行工具更改视频的分辨率&#xff08;或调整视频的大小/缩放&#xff09;。 更改视频的分辨率&#xff08;也称为调整大小或缩放&#xff09;是视频编辑、处理和压缩中非常常见的操作。对于 ABR 视频流尤其如此&#…

【笔记】Helm-3 主题-6 Chart仓库指南

Chart仓库指南 本节介绍如何创建和使用chart仓库。在高层级中&#xff0c;chart仓库是打包的chart存储和分享的位置。 社区的Helm chart仓位于 Artifact Hub &#xff0c;欢迎加入。不过Helm也可以创建并运行您自己的chart仓库。该指南将介绍如何操作。 Artifact Hub 先决条…

防爆气象站需要如何维护

TH-FBCQX2 在工业生产中&#xff0c;防爆气象站是保障安全生产的重要设备之一。由于其特殊的使用环境和功能&#xff0c;防爆气象站的维护保养工作显得尤为重要。 一、日常维护保养 清洁&#xff1a;防爆气象站的外部和内部组件需要定期清洁&#xff0c;以去除灰尘、油渍和杂质…

快速入门:搭建宠物用品小程序商城的必备知识

小程序商城逐渐成为商家展示和销售产品的重要渠道。对于宠物用品商家来说&#xff0c;搭建一个宠物用品小程序商城不仅可以提高品牌知名度&#xff0c;还能吸引更多的潜在客户。本文将介绍如何通过乔拓云平台搭建宠物用品小程序商城。 首先&#xff0c;商家需要登录乔拓云平台后…

GaussDB与openGauss有什么相同和不同?

众所周知&#xff0c;GaussDB是华为自主创新研发的分布式关系型数据库&#xff0c;为企业提供功能全面、稳定可靠、扩展性强、性能优越的企业级数据库服务&#xff0c;openGauss是开源数据库&#xff0c;两者之间又是什么样的关系&#xff0c;有什么相同和不同&#xff0c;让我…

Git教程学习:03 记录每次更新到仓库

文章目录 1 检查当前文件状态2 跟踪新文件3 暂存已修改的文件4 状态简览5 忽略文件6 查看已暂存和未暂存的修改7 提交更新8 跳过使用暂存区域9 移除文件10 移动文件 现在我们的机器上有了一个 真实项目 的 Git 仓库&#xff0c;并从这个仓库中检出了所有文件的 工作副本。 通常…

【技术科普】什么是AI视频识别分析?干货满满!

视频AI识别分析是指利用人工智能技术对视频数据进行智能化检测、分析和提取有用信息的过程。通过视频AI分析&#xff0c;可以自动化地识别、检测和理解视频中的对象、动作、场景等元素&#xff0c;并进行标记或者相关处理&#xff0c;最后形成相应事件的处理和告警信息。这项技…

软件是什么?前端,后端,数据库

软件是什么&#xff1f; 由于很多东西没有实际接触&#xff0c;很难理解&#xff0c;对于软件的定义也是各种各样。但是我还是不理解&#xff0c;软件开发中的前端&#xff0c;后端&#xff0c;数据库到底有什么关系呢&#xff01; 这个问题足足困扰了三年半&#xff0c;练习时…

分布式ID(2):雪花算法生成ID

1 雪花算法简介 这种方案大致来说是一种以划分命名空间(UUID也算,由于比较常见,所以单独分析)来生成ID的一种算法,这种方案把64-bit分别划分成多段,分开来标示机器、时间等,比如在snowflake中的64-bit分别表示如下图(图片来自网络)所示: 41-bit的时间可以表示(1L&l…

双线逆变器之低压转高压DC-DC拓扑结构

这是一个升压的过程&#xff0c;电池电压48V经过变压器等一系列的元器件最后升压到400V 如何让变压器形成正负电压&#xff1f; 通过让Q6Q7开通&#xff0c;Q6Q8关闭形成上下- 通过让Q6Q7关闭&#xff0c;Q6Q8开通形成上-下 前面四个管子和变压器的作用就是类似一个方波发生…

解决There is insufficient memory for the Java Runtime Environment to continue.

文章标题 问题描述原因分析解决方案其他可能的解决方案资料参考 问题描述 当我们使用Maven给项目打包时&#xff0c;有时候会报以下错误信息 # There is insufficient memory for the Java Runtime Environment to continue. # # Native memory allocation (malloc) failed t…

PGSQL主键序列

PostgreSQL和 MySQL数据库还是有一定的区别。 下面了解一下 PGSQL的主键序列。 一、主键 1、系统自带主键序列 在 PostgreSQL 中&#xff0c;GENERATED BY DEFAULT 和 GENERATED ALWAYS 是用于定义自动生成的列&#xff08;Generated Column&#xff09;的选项。一般可作用…

西门子燃烧控制器维修LMV37.410A2WH

西门子燃烧控制器维修范围包括&#xff1a; LMV系列燃烧器控制系统维修 LMV5系列控制器维修 AZL系列显示操作单元维修 QRI系列火焰探测器维修 SQM4系列电动执行机构维修 AZL系列或其他控制系统维修或设置燃烧器的启停&#xff0c;燃料&#xff0c;运行模式&#xff0c;运行…

Acrel-1000DP分布式光伏系统在某重工企业18MW分布式光伏中应用

摘 要&#xff1a;分布式光伏发电特指在用户场地附近建设&#xff0c;运行方式以用户侧自发自用、余电上网&#xff0c;且在配电系统平衡调节为特征的光伏发电设施&#xff0c;是一种新型的、具有广阔发展前景的发电和能源综合利用方式&#xff0c;它倡导就近发电&#xff0c;就…

SQL 最大连续合格次数 最大连胜记录次数 最大连败记录次数

有这样一个问题&#xff0c;工厂中要统计某个供应商送货检验的情况&#xff0c;依照其连续合格次数&#xff0c;决定是否免检&#xff0c;不使用游标或者循环&#xff0c;如何写这个sql。 此情景也可以用于统计连胜记录等 先要学习一下 窗函数LAG&#xff0c;指的是按分组和排…