lnk200无法解析的外部符号_C语言必学之内部链接和外部链接

首先让我们定义一下:

范围:标识符的范围是程序的一部分,可以直接访问标识符。在C语言中,所有标识符都在词法(或静态)范围内。

链接:链接描述了名称在整个程序或一个翻译单元中如何引用或不引用同一实体。以上听起来与Scope相似,但事实并非如此。要了解上述含义,让我们深入研究编译过程。

翻译单元:翻译单元是一个包含源代码,头文件和其他依赖项的文件。所有这些源都组合在一个文件中,因为它们用于产生一个单个可执行对象。以有意义的方式将源链接在一起很重要。例如,编译器应该知道printf定义位于stdio头文件中。

725d926d90f3f14730751aaaf0a6a4cc.gif

在C和C ++,即由多个源代码文件中的程序被编译一次一个。在编译过程之前,可以通过变量的作用域来描述变量。只有在链接过程开始时,链接属性才起作用。因此,范围是由编译器处理的属性,而链接是由链接器处理的属性。

链接器在编译过程的链接阶段将资源链接在一起。链接器是一个程序,它将多个机器代码文件作为输入,并生成可执行的目标代码。它解析符号(即,获取符号的定义,例如“ +”等。)并将对象排列在地址空间中。

链接是一个属性,它描述链接器应如何链接变量。变量应该可供另一个文件使用吗?应该只在声明的文件中使用变量吗?两者都是由链接决定的。
因此,链接使您可以将每个文件的名称耦合在一起,范围决定了这些名称的可见性。
有两种类型的链接:

1f20e48ecfaa58e8efd2ed9290684b7a.gif

1.内部链接:实现内部链接的标识符无法在声明它的翻译单元外部访问。该单元内的任何标识符都可以访问具有内部链接的标识符。它由关键字实现static。内部链接的标识符存储在RAM的初始化或未初始化段中。(注意: static 在引用范围方面也有含义,但此处不做讨论)。
一些例子:

1acb0d55ac9dcacf265ec0569ce03dfc.png

上面的代码实现了对标识符的静态链接animals。考虑Feed.cpp位于同一翻译单元中。

938a6789de546a9ee878ce7ea048b5d4.png

首先编译Animals.cpp,然后再编译Feed.cpp,我们得到:输出:5 8 2。

现在,考虑Feed.cpp位于另一个翻译单元中。仅当我们使用时,它才会像上面那样编译和运行#include "Animals.cpp"。
考虑位于第三个翻译单元中的Wash.cpp。

51018ef279008ad268265c269122732a.png

编译后,我们得到:输出:5 8 have fun 10。

这使我们得出以下结论:每个翻译单元都访问自己的副本animals。这就是为什么我们必须animals为= 8 Animals.cpp,animals= 2Feed.cpp和animals= 10 Wash.cpp。一份文件。此行为会消耗内存并降低性能。

内部链接的另一个属性是,仅当变量具有全局作用域时才实现内部链接,并且所有常量默认情况下都是内部链接的。

用法: 众所周知,内部链接变量是通过副本传递的。因此,如果头文件具有功能,fun1()并且头文件所包含的源代码也具有fun1()不同的定义,则这两个功能将不会相互冲突。因此,我们通常使用内部链接从全局范围隐藏翻译单元本地助手功能。例如,我们可能在一个文件中包含一个头文件,该头文件包含一种从用户读取输入的方法,该文件可以描述另一种从用户读取输入的方法。当链接时,这两个功能彼此独立。

f24ab13a4682e4312e250cd809550af5.gif

2.外部链接:实现翻译的标识符对每个翻译单元都是可见的。外部链接的标识符在翻译单元之间共享,并且被认为位于程序的最外层。实际上,这意味着您必须在所有人都可见的位置定义一个标识符,以使它只有一个可见的定义。它是全局作用域变量和函数的默认链接。因此,具有外部链接的特定标识符的所有实例都引用程序中的相同标识符。关键字extern实现外部链接。

当使用关键字时extern,我们告诉链接器在其他地方查找定义。因此,外部链接标识符的声明不会占用任何空间。Extern标识符通常存储在RAM的初始化/未初始化或文本段中。

在继续以下示例之前,请一定通过C中的理解extern关键字进行。
可以extern在局部范围内使用变量。这将进一步概述链接和范围之间的差异。考虑以下代码:

6e343b8ea51ffdd24de316bd04e2f1bd.png

说明:变量即使b在变量中也具有函数的局部作用域。注意编译发生在链接之前。也就是说,作用域是一个只能在编译阶段使用的概念。程序编译后,没有“变量范围”这样的概念。fooextern

在编译期间,b将考虑的范围。它在中具有本地范围foo()。当编译器看到该extern声明时,它相信在b某处有一个定义,并让链接程序处理其余部分。

但是,同一编译器将遍历该bar()函数并尝试查找variable b。由于b已经声明extern,因此编译器尚未为其提供内存;它尚不存在。编译器将让链接器b在翻译单元中找到的定义,然后链接器将分配b在definition中指定的值。只有这样,它b才会存在并被分配内存。但是,由于在时bar(),甚至在全局范围内都没有在编译时给出声明,因此编译器抱怨上面的错误。

鉴于确保所有变量都在其作用域内使用是编译器的工作,因此它b在in范围内声明bar()时会抱怨bin foo()。编译器将停止编译,并且程序不会传递给链接器。

我们可以b通过将第1行移到foo的定义之前,声明为全局变量来修复程序。

我们可以看下一段代码:

8d2d6536b87b54d30f72c185f1ca417a.png

我们可以通过观察外部链接的行为来解释输出。我们定义两个变量x,并z在范围内。默认情况下,它们两个都具有外部链接。现在,当我们宣布y的extern,我们告诉存在一个编译器y使用相同的翻译单元内的一些定义。请注意,这是在编译时阶段,在此阶段编译器信任extern关键字并编译程序的其余部分。下一行 extern int z 对无效z,因为 z 在我们将其声明为程序外部的全局变量时,默认情况下是外部链接。当我们遇到printfline时,编译器会看到3个变量,所有3个变量之前都已声明,并且所有3个变量都在其作用域内使用(在printf功能)。这样,即使编译器不知道的定义,程序也可以成功编译。

下一阶段是链接。链接器遍历已编译的代码,然后查找x和查找z。由于它们是全局变量,因此默认情况下它们是外部链接的。然后链接器更新的价值x,并z在整个翻译单元为10和5,如果有任何引用x,并z在翻译单元的任何其他文件,将它们设置为10和5。

现在,链接器进入extern int y并尝试y在翻译单元中查找的任何定义。它会检查翻译单元中的每个文件,以查找的定义y。如果找不到任何定义,则将引发链接器错误。在我们的程序中,我们给定了outside的定义main(),该定义已经为我们编译。因此,链接器找到该定义并更新y。

好的,很多已经给您说完了,免费领取学习资料以及教程请私信我或者点击下方了解更多链接:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值