什么是外部链接和内部链接?


337

我想了解外部链接和内部链接及其区别。

我也想知道

const除非另外声明为,否则默认情况下变量内部默认链接extern

Answers:


278

编写实现文件(.cpp.cxx等)时,编译器会生成翻译单元。这是实现中的源文件以及其中包含的所有标头#include

内部链接仅指翻译单元范围内的所有内容。

外部链接是指存在于特定翻译单元之外的事物。换句话说,可以通过整个程序访问,这是所有翻译单元(或目标文件)的组合。


112
除一个小故障外,我将对此进行表决:翻译单元不是“某种程度上是目标文件”,而是编译器从中创建目标文件的源代码。
sbi

4
@FrankHB,答案遗漏的“更重要的内容”是什么?
数学家

2
@数学家很抱歉……我认为问题应该很明显(除了措辞的准确性外)。这个答案是不完整的,因为const这里完全遗漏了有关变量规则(及其目的)的问题。
FrankHB

293

正如dudewat所说, 外部链接意味着在整个程序中都可以访问符号(函数或全局变量),而内部链接意味着只能在一个翻译单元中对其进行访问。

您可以使用externstatic关键字来显式控制符号的链接。如果未指定链接,则默认链接extern用于非const符号,static(内部)用于const符号。

// in namespace or global scope
int i; // extern by default
const int ci; // static by default
extern const int eci; // explicitly extern
static int si; // explicitly static

// the same goes for functions (but there are no const functions)
int foo(); // extern by default
static int bar(); // explicitly static 

请注意,static与其使用内部链接,不如使用匿名名称空间,也可以将classes 放入其中。在C ++ 98和C ++ 11之间,用于匿名名称空间的链接已更改,但是主要的问题是它们无法从其他翻译单元访问。

namespace {
   int i; // external linkage but unreachable from other translation units.
   class invisible_to_others { };
}

11
关键字“ export”的实现突出显示了声明为“静态”的函数与未命名空间中声明的函数之间的区别。总而言之,作为一个两阶段查找的结果,在一个翻译单元中用export关键字声明的函数模板可以引用在另一个翻译单元的未命名空间中定义的函数。(ddj.com/showArticle.jhtml?articleID=184401584)–
理查德·科登

如果我执行以下操作怎么办:1.cpp <code> const int ci; </ code> 2.cpp <code> extern const int ci; </ code>
Rajendra Uppal 2010年

2
@Rajenda,您会收到未解决的符号错误(对不起,九个月的回答延迟时间我错过了此评论)。
Motti 2010年

4
可能会大大增强此答案的信息:1)C ++ 11中不再弃用static。2)默认情况下,C ++ 11中的匿名名称空间成员具有内部链接。见stackoverflow.com/questions/10832940/...
KLAIM

2
什么是“外部联系但无法从其他翻译单位获得”?如何无法到达却仍然是外部的?
szx 2014年

101
  • 全局变量默认具有外部链接。通过extern在另一个文件中提供匹配的声明,可以将其范围扩展到除包含它之外的文件。
  • 通过在声明的前面加上关键字,可以将全局变量的范围限制为包含声明的文件static。据说这些变量具有内部联系

考虑以下示例:

1.cpp

void f(int i);
extern const int max = 10;
int n = 0;
int main()
{
    int a;
    //...
    f(a);
    //...
    f(a);
    //...
}
  1. 函数的签名f声明f为具有外部链接的函数(默认)。必须稍后在此文件或其他翻译单元(如下所示)中提供其定义。
  2. max被定义为整数常量。常量的默认链接是internal。它的链接通过关键字更改为external extern。所以现在max可以在其他文件中访问了。
  3. n被定义为整数变量。在函数体外部定义的变量的默认链接为external

2.cpp

#include <iostream>
using namespace std;

extern const int max;
extern int n;
static float z = 0.0;

void f(int i)
{
    static int nCall = 0;
    int a;
    //...
    nCall++;
    n++;
    //...
    a = max * z;
    //...
    cout << "f() called " << nCall << " times." << endl;
}
  1. max被宣布有外部联系max(带有外部链接)的匹配定义必须出现在某些文件中。(如1.cpp中所示)
  2. n被宣布有外部联系
  3. z定义为具有内部链接的全局变量。
  4. nCall指定的定义是nCall在对function的调用中保留其值的变量f()。与具有默认自动存储类的局部变量不同,nCall它将在程序启动时仅初始化一次,而对于的每次调用均不会初始化一次f()。存储类说明符static影响局部变量的生存期,而不影响其范围。

注意:关键字static起着双重作用。在全局变量的定义中使用时,它指定内部链接。当在局部变量的定义中使用时,它指定变量的生存期将是程序的持续时间,而不是函数的持续时间。

希望有帮助!


2
重要的是,当在局部变量的定义中使用时,static允许进行延迟的单次初始化(如果您需要全局对象,但由于全局构造顺序问题而必须控制何时构造它,并且不能动态分配它,这可能会很有用使用new更深入的初始化方案可能超出了所讨论对象的必要范围;这意味着,这主要是在使用C ++的嵌入式系统上的问题)。
JAB 2015年

1
很好的考试,让我开心。
Blood-HaZaRd

28

用“ C”表示(因为static关键字在“ C”和“ C ++”之间具有不同的含义)

让我们来谈谈“ C”中的不同范围

范围:基本上,我可以看到多长时间以及多远。

  1. 局部变量:作用域仅在函数内部。它位于RAM的STACK区域中。这意味着每次调用一个函数时,该函数的所有变量(包括函数参数)都会重新创建,并在控件退出该函数后销毁。(因为每次函数返回时堆栈都会被刷新)

  2. 静态变量:此范围适用于文件。可以在
    声明文件的任何位置对其进行访问。它位于RAM的DATA段中。由于只能在文件内部进行访问,因此只能进行内部链接。
    其他任何文件都看不到该变量。实际上,STATIC关键字是我们可以
    在“ C”中引入某些级别的数据或函数的唯一方法

  3. 全局变量:此范围适用于整个应用程序。它可以从应用程序的任何位置访问。全局变量也驻留在DATA段中,因为可以在应用程序中的每个位置访问它,因此可以进行EXTERNAL链接

默认情况下,所有功能都是全局的。如果需要从外部隐藏文件中的某些功能,则可以在该功能的前面加上static关键字。:-)


12
@Libin:至于1)局部变量不必在堆栈上-它们通常在堆栈上,但可以在寄存器中,而在ARM环境中,它们在寄存器中的频率比在堆栈中的频率更高(取决于某些因素-调用级别,数量正式参议员..)
Artur

4
@Libin:至于1)如果您认为“刷新”被覆盖-这是错误的。堆栈指针只是移动到其他位置。没有“清除” /清除“以前有效的本地变量”。将变量作用域与存储持续时间混合在一起。范围告诉您从哪里可以访问var。存储持续时间表明它存在多长时间。您可以使用具有静态存储持续时间的局部变量。这意味着它“永远”存在,但是可以从声明它的函数中进行访问
。– Artur

2
对不正确的概念和明显的误解投下反对票。严格来说,在C中没有定义“全局”或“变量”(作为名词)。您可能想引用“文件作用域对象”而不是“全局变量”,而是谈论“作用域”(在C中是标识符的属性)毫无意义。(两个术语在C ++中的定义都具有不同的含义。)
FrankHB

@Artur我想您会忘记“ ”中的“ 唯一 ”,这意味着它“永远”存在,但可以(仅)从声明该功能的函数中进行访问。 ”-这是一个重要的细节,因此,我想指出明确指出。
RobertS

1
@RobertSsupportsMonicaCellio-你是对的
Artur

14

在谈论这个问题之前,最好准确地了解术语翻译单元程序和C ++的一些基本概念(实际上,链接通常是其中之一)。您还必须知道什么是范围

我会强调一些要点,特别是。先前答案中缺少的那些。

链接名称的属性,由声明引入。不同的名称可以表示相同的实体(通常是对象或函数)。因此,谈论实体的链接通常是胡说八道,除非您确定该实体将仅由某些特定声明(不过通常是一个声明)中的唯一名称引用。

请注意,对象是实体,但变量不是。在讨论变量的链接时,实际上要关注所表示实体的名称(由特定声明引入)。名称的链接是以下三种之一:无链接,内部链接或外部链接。

不同的翻译单元可以通过头文件/源文件共享相同的声明(是的,这是标准的措词)。因此,您可以在不同的翻译单位中使用相同的名称。如果声明的名称具有外部链接,则该名称引用的实体的身份也将共享。如果声明的名称具有内部链接,则不同翻译单位中的相同名称表示不同的实体,但是您可以在同一翻译单位的不同范围内引用该实体。如果名称没有链接,则根本无法从其他范围引用该实体。

(糟糕...我发现我键入的内容只是在重复标准措辞 ...)

语言规范中还没有涵盖其他一些令人困惑的地方。

  1. 可见性(名称)。它也是声明名称的属性,但其含义不同于linkage
  2. 可见性(副作用)。这与本主题无关。
  3. (符号的)可见性。实际实现可以使用此概念。在这样的实现中,在目标(二进制)代码中具有特定可见性的符号通常是从实体定义映射的目标,该实体定义的名称在源(C ++)代码中具有相同的特定链接。但是,通常不能保证一对一。例如,动态库映像中的符号只能从源代码内部在该映像中指定为共享(涉及某些扩展,通常,__attribute__或者__declspec)或编译器选项,并且图像不是从翻译单元翻译的整个程序或目标文件,因此没有标准概念可以准确地描述它。因为符号不是C ++中的规范术语,所以它仅是实现细节,即使方言的相关扩展可能已被广泛采用。
  4. 辅助功能。在C ++中,这通常是关于类成员或基类的属性的,这又是与主题无关的不同概念。
  5. 全球。在C ++中,“全局”是指全局名称空间或全局名称空间范围的内容。后者大致相当于C语言中的文件范围。在C和C ++中,链接都与范围无关,尽管范围(如链接)也与某些声明引入的标识符(在C中)或名称(在C ++中)紧密相关。

命名空间范围const变量链接规则是特殊的(特别是与constC语言在文件范围中声明的对象不同,该对象也具有标识符链接的概念)。由于ODR由C ++强制执行,因此对于整个程序中出现的相同变量或函数,除了inline函数外,不要超过一个定义这一点很重要。如果没有这样的特殊规则const,则const用初始化程序声明变量的最简单声明(例如,用变量代替某些类似对象的宏是不可能的)。= xxx在多个翻译单元(或多个翻译单元多次包含)的标头或源文件(通常为“标头文件”)中), (虽然很少)在程序中会违反ODR,因此const


3
这个答案听起来很精通,可能非常准确(我无法判断),但是很可能不是许多在这里查找此问题而不直接阅读语言说明的人所期望的那样容易理解。至少出于我的需要,我会坚持接受的答案,但仍然感谢您对语言规范有所了解。👍🏻–
wedi

8

我认为C ++中的内部和外部链接给出了清晰简洁的解释:

转换单元是指实现(.c / .cpp)文件及其包括的所有头文件(.h / .hpp)。如果此类翻译单元内部的对象或功能具有内部链接,则该特定符号仅对该翻译单元内的链接器可见。如果对象或功能具有外部链接,则链接器在处理其他翻译单元时也可以看到它。当在全局名称空间中使用static关键字时,它强制符号具有内部链接。extern关键字导致符号具有外部链接。

编译器默认使用以下符号链接:

非常量全局变量默认情况下具有外部链接
常量全局变量默认情况下具有内部链接
函数默认情况下具有外部链接


6

链接确定具有相同名称的标识符是否引用相同的对象,功能或其他实体,即使这些标识符出现在不同的翻译单元中也是如此。标识符的链接取决于如何声明。链接有三种类型:

  1. 内部链接:标识符只能在翻译单元中看到。
  2. 外部链接:标识符可以在其他翻译单元中看到(和引用)。
  3. 没有链接:标识符只能在定义它们的范围内看到。链接不影响作用域

仅C ++:您还可以在C ++和非C ++代码片段之间建立链接,这称为语言链接

来源:IBM程序链接



1

在C ++中

文件范围内且未嵌套在类或函数内的任何变量在程序中的所有翻译单元中都是可见的。之所以称为外部链接,是因为在链接时,该名称对于该翻译单元外部的所有地方的链接器都是可见的。

全局变量和普通函数具有外部链接。

文件范围内的静态对象或函数名称是转换单元本地的。称为 内部链接

链接仅指在链接/加载时具有地址的元素。因此,类声明和局部变量没有链接。


const全局变量具有内部链接。
Blood-HaZaRd
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.