程序的编译与链接(详解)

程序的编译与链接


本章内容如下:

1:程序的翻译环境与执行环境的介绍


2:详解程序的翻译环境(编译+链接)

  • 2.1预处理阶段干了啥
  • 2.2编译阶段干了啥
  • 2.3汇编阶段干了啥
  • 2.4链接阶段干了啥

3:预处理详解

  • 预定义符号的介绍
  • #define 的介绍(宏与标识符号)
  • #与##的介绍
  • 宏与函数的对比
  • #undef的介绍

4:条件编译


5:#include文件

  • #include<>与#include""的区别
  • 条件编译在头文件包含中的使用场景介绍

以上就是本文章所要介绍的大概内容,下面让我们一起来学习以上的知识点吧!

1:程序的翻译环境与执行环境

首先我们知道计算机是只能识别二进制的文件的,而我们平常所写的c语言文件并不能够被计算机所直接识别,所以才有了程序的翻译环境

所以简单的来说:
翻译环境:是将文本文件(c语言写的源代码文件)转化为计算
机所能识别的二进制文件。
执行环境:用于执行实际代码的环境。

关于执行环境的内容
1:首先每个可执行程序都必须先加载到内存当中去,这个步骤在有操作系统的环境中,由操作系统来完成的。
2:程序的执行便开始了。开始调用main函数
3:开始执行程序的代码,开辟对应的函数栈桢,用来存放局部变量和返回地址,同时也可以使用静态的内存,用来存储静态变量的值和地址
4:程序的终止。这个终止可以包括意外的终止和正常终止main函数。


2:详解程序的翻译环境(编译+链接)
首先我们需要了解的就是翻译环境包括两个部分,编译+链接。

而编译又包括3个阶段。
1:预处理阶段
2:编译阶段
3:汇编阶段

我们在vs2019这样的集成开发环境下并不能够区别这三个阶段到底干了啥事,所以我们采用在linux环境下来介绍。

首先我们先来介绍在linux环境下的三条指令:
1: gcc -E 要编译的文件 -o 生成的文件名
意思为:当编译器编译到预处理阶段完成后就停止对程序的编译,也就
是只完成编译的预处理阶段。
2:gcc -S 要编译的文件 -o 生成的文件名
意思为:当编译器编译到编译阶段完成后就停止对程序的编译,也就
是只完成编译的编译阶段。
3:gcc -c(小写) 要编译的文件 -o 生成的文件名
意思为:当编译器编译到汇编阶段完成后就停止对程序的编译,也就
是只完成编译的汇编阶段。

首先我们先写一段代码,然后在linux系统下看看这段代码在不同的阶段完成了什么事情。代码如下:

#include<stdio.h>

//定义一个宏常量
#define M 3
#define N 2


int main()
{
	//main函数体
	int a =2;
	int b =2;
	int c =a+b+M;
	printf("%d\n",c);
	printf("%d\n",N);
	return 0;
}

在这里插入图片描述

我们能够看到在预处理阶段完成后的文件中,代码量明显就增多了
从原本的10多行到现在的800多行,并且在源文件中本来有的注释
和定义的宏,在预处理阶段完成后,都消失了。
这也从侧面告诉了我们预处理阶段会干的事情如下:

预处理阶段会干的事情
1.#include头文件的展开
2. 预定义符号的替换
3. 去除注释

注意:所有预定义符号的替换都是在预处理阶段完成的。

然后我们在来观察在编译阶段会做的事情
在这里插入图片描述

上图就是我们经过编译阶段所形成的test.s这个文件,我们发现里面并不是我们能够读懂的c语言代码了,而变成了汇编指令了。

	所以在我们的编译阶段会进行如下的过程
	1.语法分析
	2.语义分析
	3.词法分析
	4.符号汇总

编译目的:将c语言代码转化为汇编代码,并且进行符号汇总。

我们再来看一看汇编阶段到底干了啥事>

在这里插入图片描述
我们发现经过汇编所形成的文件后,文件我们就看不懂了,因为此时的文件是二进制文件。

	汇编阶段会干的事情
	1.形成符号表
	2.将汇编代码转化为计算机能够识别的二进制代码
	其实在linux系统下,gcc所产生的目标.o文件,可执行程序,
	它的文件格式为ELF这种类型的文件格式来进行存储的,
	而在linux环境下,可以用readelf命令来读取这样的文件。

关于编译阶段符号汇总与汇编阶段形成符号表的意思

符号汇总,其实本质上来说就是统计文件中所使用的函数名,这些符号
而形成符号表的意思我们可以简单的理解为:将函数与它的地址看作整
体。在链接的时候在使用这张表
如下图的意思

在这里插入图片描述

这三个步骤的完成就标志着我们翻译环境的编译过程就完成了。

链接阶段:

	gcc 源文件 -o 形成文件名
	本质上来说链接阶段就是形成可执行程序的最后一步。
	链接阶段所干的事情:
	1.合并段表
	2.符号表的合并与重定位
	
	段表的意思为:我们的每个目标文件都是按照elf的文件格式进
	行排版的,而elf会将文件划分为很多段,每一段执行不同的功
	能,而我们在一个项目中可以有很多个源文件,每一个源文件生
	成的目标文件都会按照这种格式进行分段,所以在链接的时候我
	们将具有相同功能的代码,放在同一个段表内。
	符号表的合并也是将不同源文件所包含的同一种符号进行合并,
	重定位表示的是给符号确定正确的地址。

对于符号表,段表的理解可能不是什么非常清楚,大家可以去看<编译原理这本书>


3:预处理阶段详解
1.预定义符号的介绍
预定义符号以下的几个都是语言内置的
__FILE__  //进行编译的文件
__LINE__  //文件输出这个当前的行号
__DATE__  //文件被编译的日期
__TIME__  文件被编译的时间

在这里插入图片描述

2.#define定义的标识符与宏
#define定义的标识符语法  #define name  内容 (无;)
#define M 3   //在预处理阶段只要程序中有M 就替换为3
#define DOU double// DOU-->double 
#define reg register// reg--->register

#define定义的宏
#define的一个规定,可以将参数替换到文本中去,这种实现就叫做宏。
我们通过讲解2个宏来掌握宏,并且了解一些注意事项。

#define ADD(x,y) ((x)+(y))

我们通过图片来讲解上面的宏
在这里插入图片描述
3.#与##的作用

#:可以将宏的参数化为字符串
##:可以将宏参数进行合并

直接讲语法可能有点抽象,我们通过具体的代码来进行讲解

#define PRINT(N,formate) printf("the value of 
"#N" is "formate"\n",N)
int main()
{
	/*printf("hello ""world\n");
	printf("hello world\n");*/ //这两种情况是一样的
	int a = 10;
	int b = 15;
	PRINT(a,"%d");
	PRINT(b,"%d");
	float c = 3.14f;
	PRINT(c, "%f");

	return 0;
}

在这里插入图片描述

//##在宏中的作用是将两个常数符号连接起来
#define CAT(x,y) x##y
int main()
{
	int c110 = 2024;

	int a = CAT(c, 110);
	printf("%d\n", a);


	return 0;
}

在这里插入图片描述
4.宏与函数的区别:

  1. 宏是直接对代码块进行替换的,函数则需要去掉用相应的函数。
  2. 宏不能进行调试,函数可以。
  3. 宏不能进行递归
  4. 宏没有函数参数类型的检查,可能比较危险
  5. 代码长度宏需要进行替换,所以长度大于函数的长度
  6. 运行效率 宏>函数
  7. 宏替换时,可能会涉及到操作符的优先级的问题

其次在好的编程习惯来讲,宏名一般全是大写,而函数不需要

5:#undef
作用:当我们在以后的代码中不需要在使用对应的宏的时候,我们可以用#undef 来去除对应的宏

命令行定义:我们可以在编译的过程中定义符号,
命令为 : gcc 编译文件 -D 符号定义

5:条件编译
在编译一个程序的时候我们需要放弃一条语句或者一组语句是非常容易
的,因为我们有条件编译指令
常见的条件编译指令有以下一些:
如:
1.单分支
#if 常量表达式
....
#endif
2.多分支
#if 常量表达式
........
#elif 常量表达式
......
#else
......
#endif
这两种条件编译的指令当表达式为真时就会保留对应的代码,比如说:

#define x 10
int main()
{
	
#if x==10
	printf("haha\n");
#elif x==2
	printf("hehe\n");
#else
	printf("heihei\n");
#endif

	return 0;
}

我们在linux系统的环境下进行查看。
在这里插入图片描述
应为我们定义了x=10,所以保留了haha


如果定义了符号则保留代码
#ifdef  symbol
......
#endif

//如果没有定义则保留代码
#ifndef symbol
....
#endif
这个语句是看我们定义了符号没,只要定义了就保留,我们在linux
下看一看

在这里插入图片描述


5#include文件的包含

1.#include<> 与#include“”文件的区别

		#include<>,一般来说这是包含库里面的文件,而""是包
		含我们自己所写的头文件。
		这两者的不同存在与:#include<>会直接去指定的标准路径
		下去进行查找。
		而#include“”会先在与源文件相同的路径下查找,如果没有
		找到则会想寻找库函数头文件的方式到标准的指定文件下去
		进行查找。
  1. 条件编译在头文件包含中的使用场景介绍
    我们知道当我们有许多源文件和头文件的时候,因为我们会包含对应的头文件,可能会导致在一个文件中可能会出现引入多个相同的头文件,而在预处理阶段头文件又会进行展开,导致代码的长度会持续的增加,所以这时候就需要我们的条件编译出场了
#ifndef __TEST.H__
#define __TEST.H__
#endif
或者
#pragma once
这样就可以避免头文件的多次包含了。

到这里本章就结束了,感谢大家的观看。

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

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

相关文章

【MinIO】几个有用的方法

在windows总安装Minio 这是一篇不错的安装指南 进入网址 在Windows安装时&#xff0c;选择相应的exe文件下载&#xff0c;下载到本地后&#xff0c;使用如下的命令即可在前台启动&#xff1a; minio.exe server D:\your_path 或者将该路径写进环境变量的path中&#xff0c;…

怎么当代课老师教学生

老师朋友们&#xff0c;有没有帮忙当过代课老师呢&#xff1f;或者&#xff0c;没当过的老师是不是对这种职业充满了好奇&#xff1f;让我来分享一下&#xff0c;当代课老师的日常是什么样的吧&#xff01; 备课 说起备课&#xff0c;那可是个大工程&#xff01;不过&#xff…

微信消息推送说明

1 打开任务清单 2 编辑任务清单设置 名字解释 姓名&#xff1a;微信名字 内容&#xff1a;要发送消息 定时&#xff1a;从几点开始发送 每隔几分钟&#xff1a;每隔几分钟重复发送一次 重复次数&#xff1a;每隔几分钟重复发送几次 响玲&#xff1a;定时语音电话&#x…

掌握高效性能测试技能:JMeter基础入门!

一、JMeter基础 A、JMeter介绍 Apache JMeter是Apache组织开发的基于Java的压力测试工具。 Apache JMeter may be used to test performance both on static and dynamic resources (files, Servlets, Perl scripts, Java Objects, Data Bases and Queries, FTP Servers and …

Unity UGUI的自动布局-LayoutGroup(水平布局)组件

Horizontal Layout Group | Unity UI | 1.0.0 1. 什么是HorizontalLayoutGroup组件&#xff1f; HorizontalLayoutGroup是Unity UGUI中的一种布局组件&#xff0c;用于在水平方向上对子物体进行排列和布局。它可以根据一定的规则自动调整子物体的位置和大小&#xff0c;使它们…

机器人规划算法——movebase导航框架源码分析

这里对MoveBase类的类成员进行了声明&#xff0c;以下为比较重要的几个类成员函数。 构造函数 MoveBase::MoveBase | 初始化Action 控制主体 MoveBase::executeCb收到目标&#xff0c;触发全局规划线程&#xff0c;循环执行局部规划 全局规划线程 void MoveBase::planThread |…

[黑马程序员SpringBoot2]——原理篇1

目录&#xff1a; bean的加载方式(—)bean的加载方式(二)bean的加载方式(三)FactoryBeanproxyBeanMethod属性bean的加载方式(四)bean的加载方式(五)bean的加载方式(六)bean的加载方式(七)bean的加载方式(八)bean加载控制&#xff08;编程式)bean加载控制&#xff08;注解式)be…

前缀和+哈希表——560. 和为 K 的子数组

文章目录 &#x1fa90;1. 题目&#x1f31f;2. 算法原理⭐解法一&#xff1a;暴力枚举⭐解法二&#xff1a;前缀和哈希表 &#x1f31e;3. 代码实现 &#x1fa90;1. 题目 题目链接&#xff1a;560. 和为 K 的子数组 - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组…

每日一题:LeetCode-102.二叉树的层序遍历

每日一题系列&#xff08;day 03&#xff09; 前言&#xff1a; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f308; &#x1f50e…

SAP smartform 实现打印条形码

先在SE73里定义一个新的BARCODE&#xff0c;注意一定要用新的才可以&#xff0c;旧的是打印不出来的。 然后定义一个SMARTFORM的样式&#xff0c;把你定义的BARCODE放到字符样式里面去。 再做SMARTFORM就可以了&#xff0c;将需要作为条码的变量的格式选为该BARCODE格式&…

是否有无限提取的代理IP?作为技术你需要知道这些

最近有互联网行业的技术小伙伴问到&#xff0c;有没有可以无限提取的代理IP&#xff1f;就是比如我一秒钟提取几万、几十万次&#xff0c;或者很多台机器同时调用API提取链接&#xff0c;这样可以吗&#xff1f;看到这个问题&#xff0c;不禁沉思起来&#xff0c;其实理论上是存…

cocos游戏引擎,弹出框浏览器正常,但到了抖音、微信小游戏就不显示的bug原因及解决办法

本篇文章主要讲解&#xff1a;cocos游戏引擎&#xff0c;浏览器测试时弹出框好好的&#xff0c;无任何报错&#xff0c;构建项目到抖音、微信小游戏时无法弹出弹出框&#xff0c;但又无报错的问题原因及解决办法。 日期&#xff1a;2023年11月25日 作者&#xff1a;任聪聪 问题…

linux系统中select函数的用法实现

前言&#xff1a; select机制已经被很多人都讲解过&#xff0c;select使用起来也不是特别难&#xff0c;为什么还要花时间再次讲解select机制&#xff1f; 在回答这个问题之前&#xff0c;我们先问一下自己&#xff0c;是否有足够的信心保证在使用select编程时不出错&#xf…

【数字图像处理】均值滤波与中值滤波

在数字图像处理中,均值滤波和中值滤波是常见的空间域处理方法,可以用于过滤图像中的噪声。本文主要介绍数字图像均值滤波与中值滤波的基本原理,并记录在紫光同创 PGL22G FPGA 平台的布署与实现过程。 目录 1. 均值滤波与中值滤波 2. FPGA 布署与实现 2.1 功能与指标定义

C语言 - 基础

C 语言 1. Hello World #include <stdio.h>int main(int argc, const char *argv[]) {printf("hello world\n");return 0; }注意: 所有的标点符号必须在英文状态下输入单词不要写错注意空格 创建 C语言 程序步骤&#xff1a; 1、创建一个文档&#xff0c;以…

MYSQL 及 SQL 注入

文章目录 前言什么是sql注入防止SQL注入Like语句中的注入后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;Mysql &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果出现…

「最优化基础知识2」一维搜索,以及python代码

最优化基础知识&#xff08;2&#xff09; 无约束优化问题&#xff0c;一维搜索 一、一维搜索 一维搜索的意思是在一个方向上找到最小点。 用数学语言描述&#xff0c;X*Xk tPk&#xff0c;从Xk沿着Pk方向行走t到达最小点X*。 1、收敛速度&#xff1a; 线性收敛&#xff1…

mac测试远程端口是否可连接

打开命令行工具&#xff0c;使用命令nc -z ip port即可 &#xff0c;如果成功&#xff0c;则会返回如下信息&#xff1a; 。

FANUC机器人系统配置相关--系统变量介绍

FANUC机器人系统配置相关–系统变量介绍 系统配置页相关变量 1- 停电处理$SEMIPOWERFL = TRUE(有效)/FALSE(无效) 2- 停电处理中的I/O $PWF_IO = 1(不恢复)/2(仿真恢复)/3(解除仿真)/4(恢复所有) 3- 停电处理无效时自动执行的程序 $PWR_NORMAL = ‘’ 4- 停电处理有效时自动…

【21年扬大真题】编写程序,通过指针p的改变,实现一维数组的输入及逆序输出

【21年扬大真题】编写程序&#xff0c;通过指针p的改变&#xff0c;实现一维数组的输入及逆序输出 例如&#xff0c;输入为1,2,3,4,5,6,7&#xff1b; 输出为7,6,5,4,3,2,1 法一&#xff1a;不改变原数组&#xff0c;仅逆序打印输出 #define _CRT_SECURE_NO_WARNINGS #includ…