编译和链接(上)

简介: 编译和链接(上)

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

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;
}

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

提示:

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

目录
相关文章
|
6月前
|
存储 自然语言处理 编译器
|
6月前
|
存储 自然语言处理 编译器
编译和链接(翻译环境:预编译+编译+汇编+链接​、运行环境)
编译和链接(翻译环境:预编译+编译+汇编+链接​、运行环境)
|
1月前
|
存储 自然语言处理 编译器
|
6月前
|
存储 编译器 Linux
什么是编译与链接呢!
什么是编译与链接呢!
44 0
|
5月前
|
存储 自然语言处理 前端开发
编译与链接(想了解编译与链接,那么看这一篇就足够了!)
编译与链接(想了解编译与链接,那么看这一篇就足够了!)
|
5月前
|
存储 自然语言处理 C语言
编译和链接
编译和链接
24 0
|
11月前
|
存储 自然语言处理 算法
程序的编译和链接
程序的编译和链接
48 0
|
6月前
|
自然语言处理 编译器 C语言
C语言程序编译和链接
在ANSI C的任何⼀种实现中,存在两个不同的环境。 第1种是翻译环境,在这个环境中源代码被转换为可执⾏的机器指令(⼆进制指令)。 第2种是执⾏环境,它⽤于实际执⾏代码。
31 0
|
编译器
编译和链接(下)
编译和链接(下)
55 0
|
编译器 程序员 C语言
C++编译器和链接器的完全指南
C++是一种强类型语言,它的编译和链接是程序开发过程中不可或缺的两个环节。编译器和链接器是两个非常重要的概念。本文将详细介绍C++中的编译器和链接器以及它们的工作原理和使用方法。
467 0