编译和链接(上)

简介: 编译和链接(上)

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

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

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

第2种是执行环境,它用于实际执行代码。

翻译环境

翻译环境一般需要两个工作量就是编译和链接。

组成一个程序的每个源文件通过编译过程分别转换成目标代码(object code)。

每个目标文件由链接器(linker)捆绑在一起,形成一个单一而完整的可执行程序。

链接器同时也会引入标准C函数库中任何被该程序所用到的函数,而且它可以搜索程序员个人

的程序库,将其需要的函数也链接到程序中。

编译

编译对于vs2022来说,是由cl.exe完成的。编译又有三个阶段,很难给大家演示出来,便只说出来了解一下即可。分为,预编译(预处理)、编译、汇编。

预处理会把test.c文件生成一个test.i文件完成了主要三个功能:

1.注释的替换,替换成大空格,在test.i文件中不会显示注释。

2.头文件的包含。把头文件变成具体的代码引入。

3.define的替换。

编译,会把test.i文件具体实现为汇编代码也就是一个test.s文件,做到了例如:

词法分析、语法分析、语义分析、符号分析。

最后汇编会把test.i文件实现为test.o(test.obj)文件,生成了符号表。

链接

链接主要就是链接目标文件和链接库生成可执行的二进制程序。

具体做到了:

符号表的生成、符号表的合并和重定位。

执行环境

程序执行的过程:

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

预处理详解

预定义符号

主要有:

FILE //进行编译的源文件

LINE //文件当前的行号

DATE //文件被编译的日期

TIME //文件被编译的时间

STDC //如果编译器遵循ANSI C,其值为1,否则未定义

用代码实现来看一下具体作用:

#include<stdio.h>
int main()
{
  printf("进行编译的源文件:%s\n", __FILE__);
  printf("文件当前的行号:%d\n", __LINE__);
  printf("文件被编译的日期:%s\n", __DATE__);
  printf("文件被编译的时间:%s\n", __TIME__);
  return 0;
}

可以看一下和我电脑右下方的时间是一样的。

#define

这个定义标识符我们经常用到,那么提一个问题在使用完要不要加一个分号呢?

答案是不。我们来看一下为什么:

#define MAX 1000;
#define MAX 1000

建议不要加上 ; ,这样容易导致问题。

比如下面的场景:

if(condition)
 max = MAX;
else
 max = 0;

这里会出现语法错误。如果使用的是第一个那么会有两个分号,第二个则是没有语法问题。

#define 定义宏

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

宏(define macro)。

下面是宏的申明方式:

#define name( parament-list ) stuff

其中的 parament-list 是一个由逗号隔开的符号表,它们可能出现在stuff中。

注意:

参数列表的左括号必须与name紧邻。

如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。

宏在使用的时候最好每个参数都加一个(),至于为什么我们直接上代码看原因。

#define SQE(x) x*x
int main()
{
  int a = 5;
  printf("a+1的平方是:%d", SQE(a + 1));
  return 0;
}

这样发生的原因就是因为宏只会替换过去,这个时候而因为符号优先级可能会导致意外的错误。

为了尽量避免这样的问题发生,我们可以把每个参数带上()。

#define SQE(x) (x)*(x)
int main()
{
  int a = 5;
  printf("a+1的平方是:%d", SQE(a + 1));
  return 0;
}

这样可以一定程度的避免因为符号优先级而导致的错误。

提示:

用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中 的操作符或邻近操作符之间不可预料的相互作用。

目录
相关文章
|
8月前
|
人工智能 分布式计算 Cloud Native
云原生数据仓库AnalyticDB:深度智能化的数据分析洞察
云原生数据仓库AnalyticDB(ADB)是一款深度智能化的数据分析工具,支持大规模数据处理与实时分析。其架构演进包括存算分离、弹性伸缩及性能优化,提供zero-ETL和APS等数据融合功能。ADB通过多层隔离保障负载安全,托管Spark性能提升7倍,并引入AI预测能力。案例中,易点天下借助ADB优化广告营销业务,实现了30%的任务耗时降低和20%的成本节省,展示了云原生数据库对出海企业的数字化赋能。
241 3
|
JSON Java Maven
Springboot快速入门
Springboot快速入门
128 0
|
12月前
|
机器学习/深度学习 人工智能 移动开发
科技云报到:从“N 号房”看Deepfake乱象,如何证明“我”不是我
科技云报到:从“N 号房”看Deepfake乱象,如何证明“我”不是我
235 12
|
搜索推荐 Android开发 UED
探索安卓开发中的自定义视图:打造个性化用户界面
【7月更文挑战第31天】在安卓应用的海洋中,一个独特且吸引人的用户界面是捕获用户眼球的关键。本文将带你深入理解如何在Android开发中创建自定义视图,从而设计出与众不同的用户界面。我们将一起探索如何从零开始构建一个自定义视图组件,并实现动态交互效果。通过实际的代码示例和详细的步骤解析,你将学会如何提升你的应用界面,让它在众多应用中脱颖而出。
208 31
|
域名解析 负载均衡 网络协议
阿里云云解析收费版和免费版有什么不同?域名解析DNS免费收费区别对比
阿里云域名解析DNS收费吗?域名解析DNS免费版和收费版有什么区别?
5791 0
阿里云云解析收费版和免费版有什么不同?域名解析DNS免费收费区别对比
|
网络协议 关系型数据库 Linux
PostGresql数据库Linux服务器安装
PostGresql数据库,Linux服务器,在线,离线安装
4369 2
PostGresql数据库Linux服务器安装
|
存储 Kubernetes Linux
Kubernetes 集群使用 GlusterFS 作为数据持久化存储
Kubernetes 集群使用 GlusterFS 作为数据持久化存储
218 0
|
存储 缓存 算法
三种常用的数据分片方式:Hash分片,一致性Hash分片和按照数据范围分片
三种常用的数据分片方式:Hash分片,一致性Hash分片和按照数据范围分片
744 0
三种常用的数据分片方式:Hash分片,一致性Hash分片和按照数据范围分片
|
数据可视化 Java 数据安全/隐私保护
【Spring Cloud Alibaba Sentinel 实现熔断与限流】 —— 每天一点小知识(上)
【Spring Cloud Alibaba Sentinel 实现熔断与限流】 —— 每天一点小知识
426 0
|
XML 开发框架 前端开发
在ASP.Net Core下,Autofac实现自动注入
在ASP.Net Core下,Autofac实现自动注入
328 0
在ASP.Net Core下,Autofac实现自动注入