翻译|C语言必学之内部链接和外部链接( 二 )


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



2.外部链接:实现翻译的标识符对每个翻译单元都是可见的 。 外部链接的标识符在翻译单元之间共享 , 并且被认为位于程序的最外层 。 实际上 , 这意味着您必须在所有人都可见的位置定义一个标识符 , 以使它只有一个可见的定义 。 它是全局作用域变量和函数的默认链接 。 因此 , 具有外部链接的特定标识符的所有实例都引用程序中的相同标识符 。 关键字extern实现外部链接 。
当使用关键字时extern , 我们告诉链接器在其他地方查找定义 。 因此 , 外部链接标识符的声明不会占用任何空间 。 Extern标识符通常存储在RAM的初始化/未初始化或文本段中 。
在继续以下示例之前 , 请一定通过C中的理解extern关键字进行 。
可以extern在局部范围内使用变量 。 这将进一步概述链接和范围之间的差异 。 考虑以下代码:


说明:变量即使b在变量中也具有函数的局部作用域 。 注意编译发生在链接之前 。 也就是说 , 作用域是一个只能在编译阶段使用的概念 。 程序编译后 , 没有“变量范围”这样的概念 。 fooextern
在编译期间 , b将考虑的范围 。 它在中具有本地范围foo() 。 当编译器看到该extern声明时 , 它相信在b某处有一个定义 , 并让链接程序处理其余部分 。
但是 , 同一编译器将遍历该bar()函数并尝试查找variable b 。 由于b已经声明extern , 因此编译器尚未为其提供内存;它尚不存在 。 编译器将让链接器b在翻译单元中找到的定义 , 然后链接器将分配b在definition中指定的值 。 只有这样 , 它b才会存在并被分配内存 。 但是 , 由于在时bar() , 甚至在全局范围内都没有在编译时给出声明 , 因此编译器抱怨上面的错误 。
鉴于确保所有变量都在其作用域内使用是编译器的工作 , 因此它b在in范围内声明bar()时会抱怨bin foo() 。 编译器将停止编译 , 并且程序不会传递给链接器 。
我们可以b通过将第1行移到foo的定义之前 , 声明为全局变量来修复程序 。
我们可以看下一段代码:


我们可以通过观察外部链接的行为来解释输出 。 我们定义两个变量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 。


推荐阅读