C++中编译器和链接器的作用

文章详细介绍了C++编程中编译和链接的过程。编译器将源代码转换为.obj文件,经过预处理、标记解释和解析阶段,生成机器码。链接器则负责整合多个.obj文件,解决符号引用,未解决的外部符号通常是链接错误的原因。解决这类问题的方法包括使用static或inline关键字,以及调整函数定义和声明。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在写代码的过程中,我们实际上是在写一个文本文件(text)。而实际中我们需要某种方法将文本转换为应用到计算机上的文件,让计算机可以听懂。从文本文件到可执行的二进制文件(binary),主要有两个操作:一是编译(compiling),二是链接(linking)。这两个阶段是独立进行的,也就是说,一个程序有可能会编译成功,但是链接报错。实际上,在错误信息中,C开头的是编译报错,LNK开头的是链接报错。

  1. 编译器(Compiler)

在C++中,编译器要做的工作就是将文本文件转换为中继格式.obj文件,也就是编译器会给每一个转换单元(translation unit),生成一个.obj文件。转换单元的含义跟cpp文件差别不大,就是在做项目中,我们倾向于在一个大的cpp文件中include其他cpp文件,然后只编译那一个,也就是只有一个转换单元。其实说白了,就是说我们所写的cpp文件一点也不重要,它其实只是用来给编译器生成.obj文件的一个初始文件。

在这个过程中,编译器要做很多工作。第一个为预处理过程(pre-processing),也就是所有预处理语句会在这个过程中被评估,常见的有include,define,if和ifdef。include语句的作用就是将指定的文件读取,并粘贴进你写include的那个文档中。例如新建一个头文件EndBrace.h,并在其中仅仅打一个花括号:

}

在这之后,写入一下代码:

int Multiply(int a, int b)
{
    int result = a * b;
    return result;
#include "EndBrace.h"

编译不会报错,其实就是因为include只是将头文件复制到了文件中。

define语句就是去搜索一个词,并将它替换成后面你设置的词,例如:

#define INTEGER int

就是将所有INTEGER替换为int。

第二个为标记解释和解析阶段(tokenizing and parsing),基本就是把C++语句处理成编译器可以看懂并处理的语言,也就是将代码转换为要么是常数资料(数据),要么是指令。在这之后,编译器就会生成真正的代码,这个代码就是CPU后面会去执行的机器码,同时,我们也会得到一些其他数据,像是存储常量的地方。

  1. 链接器(Linker)

链接的主要工作是找到每个符号和函数的位置并将它们链接到一起。在我们的项目中,一般都会有很多个obj文件被创建,这些文件需要链接器来将它们链接到一起,作为一个程序。主函数作为程序的入口,必须被定义,所以如果一个程序不存在主函数时,链接器不知道第一步要怎么走,便会报错。

一种常见的连接器错误为:未解决的外部符号(unsolved external symbol)。造成这个错误的一个主要原因是调用函数时,连接器找不到那个函数在哪里。换句话说,你有可能调用了一个不存在的函数或者这个函数的名字你打错了。值得注意的是,当你写了一个函数并且调用了另外一个不存在的函数时,即使这个函数并没有被实际使用,连接器还是会报错。这是因为虽然你并没有在这个文件中使用它,但是并不意味着它不会在别的地方被调用。解决的办法是在函数声明前加一个static关键词,来告诉链接器这是一个内部的函数,它不会在这个文件以外的地方被调用。

此外,函数的返回值也会影响函数的定义。例如,void a(const char* x)和int a(const char* x)虽然函数名字一样,但由于返回的值不一样,所以本质上是两个函数。

还有,当两个函数完全一样时,也会报错,因为链接器搞不懂要link到哪里了。当这种情况发生时,有可能是你的文件调用了这个函数但是你文件里调用的某个函数也调用了这个函数。要解决这个问题,可以在头文件函数声明时加一个static关键字,这代表着函数链接时只链接在该文件内部,即两个文件之间是单独链接的。也可以在头文件函数声明时加一个inline关键字,也就是把函数的身体拿过来来取代调用。另外一种方法就是将定义移动到其中一个翻译单元中,在头文件只留下函数的声明。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值