外部、内部和没有联系或者为啥这不起作用?
Posted
技术标签:
【中文标题】外部、内部和没有联系或者为啥这不起作用?【英文标题】:External, internal and no linkage or why this does not work?外部、内部和没有联系或者为什么这不起作用? 【发布时间】:2017-02-07 14:31:23 【问题描述】:根据C标准:
在构成整个程序的一组翻译单元和库中,每个 特定标识符的声明 外部链接 表示相同的对象或 功能。在一个翻译单元中,标识符的每个声明都带有 内部 联动 表示相同的对象或功能。标识符的每个声明 没有 联动 表示一个唯一的实体。
在我的示例中,我们有三个单独的声明,每个标识符都有不同的链接。那么为什么这不起作用呢?
static int a; //a_Internal
int main(void)
int a; //a_Local
extern int a; //a_External
return 0;
错误:
在函数'main'中: 第 9 行:错误:以前声明的变量 'static' 重新声明了 'extern'
为什么编译器坚持让我重新声明而不是尝试访问另一个文件中的外部对象?
可供参考的有效 C++ 示例:
static void f();
static int i = 0; // #1
void g()
extern void f(); // internal linkage
int i; // #2 i has no linkage
extern void f(); // internal linkage
extern int i; // #3 external linkage
Clang 和 VC 似乎都适合我的 C 示例;只有某些版本的 GCC(不是全部)会产生上述错误。
【问题讨论】:
那么,您已经阅读了第 2 段,很好,现在尝试阅读第 6 段,然后是第 4 段,即使在那之后,如果您有问题,请回来。 :) 使用 gcc 会出现此错误,但使用 clang 不会。 (有趣的事实:在 C++ 中,在 [basic.link]/6 的示例中提到了此代码(有效!)。) 我已经阅读了所有段落,但没有一次。 a_Local 没有链接,对吧。 @KerrekSB 你能修复你的链接吗?因为现在我想知道为什么 C++ 的行为不同。 【参考方案1】:§6.2.2, 7 说:
如果在一个翻译单元中,相同的标识符出现在两个 内部和外部链接,行为未定义。
所以,你的程序有undefined behaviour。
§6.2.2, 4 这么说
extern int a; //a_External
具有外部链接,因为在int a; //a_Local
范围内可见的先前声明没有链接。但是
static int a; //a_Internal
用内部链接声明a
。因此,它是 undefined 每 §6.2.2, 7.
【讨论】:
看来我没有考虑这个异常。这种说法有什么理由吗? @user6898756 理由:禁止没有明显好处的奇怪和深奥规则的难以理解的结果?只是也许?另外,也许还有更好的机会来构建正确的编译器和链接器。【参考方案2】:编译器给出这个错误是因为在a_External
范围内,a_Internal
仍然可以访问,因此你在a_External
中将a_Internal
从static
重新声明为extern
,因为@ 的名称冲突987654327@。这个问题可以通过使用不同的变量名来解决,例如:
static int a1; //a_Internal
int main(void)
int a2; //a_Local
extern int a3; //a_External
return 0;
【讨论】:
按照你的解释,a_Local 也不应该工作。 仅供参考:如果标识符在同一名称空间中指定两个不同的实体,则范围可能会重叠。如果是这样,一个实体的范围(内部范围)将严格在另一个实体的范围(外部范围)之前结束。在内部范围内,标识符指定在内部范围内声明的实体; 在外部范围内声明的实体在内部范围内隐藏(不可见)。 @user6898756 这不是它的工作原理,请参阅问题中的引用:所有带有链接的标识符声明都表示同一实体(它们不会隐藏任何其他带有链接的声明) @M.M 我指的是 在 a_External 范围内,a_Internal 仍然可以访问。不,这不对。通过 a_External 声明将其隐藏。访问 a_Internal 的唯一方法是按照我引用的相同链接规则。 @MM 引用的文本规定“具有外部链接的特定标识符的每个声明都表示相同的对象”,它还规定“具有内部链接的标识符的每个声明都表示相同的对象”。与所有自然语言一样,语义是有待商榷的,但一种可能的解释是,我们有两组截然不同且不相关的“相同”对象,它们具有内部链接;和那些有外部联系的人。 ...【参考方案3】:C 标准说:
在翻译单元集合中,每个声明都有一个特定的 具有外部链接的标识符表示相同的实体(对象或 功能)。在一个翻译单元内,每个声明 具有内部链接的标识符表示同一实体。
在翻译单元集合中,我们不能有多个不同的同名外部实体,因此表示单个外部实体的每个声明的类型应该一致。我们可以检查一个翻译单元内的类型是否一致,这是在编译时完成的。我们无法在编译时和链接时检查不同翻译单元之间的类型是否一致。
对于使用存储类说明符 extern 声明的标识符 该标识符的先前声明可见的范围,31) 如果先前的声明指定了内部或外部链接,则 在后面的声明中标识符的链接与 在先前声明中指定的链接。如果没有事先声明 可见,或者如果先前的声明没有指定链接,则 标识符有外部链接。
static int a; //a_Internal
int main(void)
int a; //No linkage
extern int a; //a_External
return 0;
这里之前声明的标识符a没有链接,所以extern int a
有外部链接。这意味着我们必须在另一个翻译单元中定义 int a 。然而,GCC 决定拒绝此代码,变量先前声明为 static
redeclared 'extern' 错误,可能是因为根据C
标准,我们有未定义的行为。
【讨论】:
以上是关于外部、内部和没有联系或者为啥这不起作用?的主要内容,如果未能解决你的问题,请参考以下文章