第三个x
应该指的是什么:
#include <stdio.h>
static char x = '1';
int main(void)
{
char x = '2';
{
extern char x;
printf("%cn", x);
}
}
这出现在这个答案中,并且:
- 在 Apple LLVM 9.1.0 clang-902-0.39.2 中,
extern char x
的x
是指第一个x
,并打印"1"。 - GCC 8.2不接受此源文本,抱怨:"错误:先前声明为'静态'的变量重新声明为'extern'"。
C 2018 6.2.2 4 说:
对于在可见该标识符的先前声明的作用域中使用存储类说明符extern声明的标识符,如果先前声明指定了内部链接或外部链接,则后面声明处的标识符链接与先前声明中指定的链接相同。如果没有先前声明可见,或者先前声明未指定链接,则标识符具有外部链接。
由于有两个先前的x
声明,以下每个"if"子句的条件都为真,第一个用于第一个先前声明,第二个用于第二个先前声明:
- 。如果先前声明指定了内部或外部链接,则在后声明中标识的链接与在先前声明中指定的链接相同。
- 。如果先前的声明未指定链接,则标识符具有外部链接。
Clang在这里的行为与使用第一句是一致的,因此第三x
具有内部链接,并引用与第一x
相同的对象。GCC在这里的行为与使用第二条是一致的,因此第三x
有外部联系,与第一x
有内联冲突。
C 标准是否为我们提供了一种解决哪种情况的方法?
第三个声明,extern char x
,应该声明带有外部链接的x
,基于 C 2018 6.2.2 4,其中说:
对于在可见该标识符的先前声明的作用域中使用存储类说明符extern声明的标识符,如果先前声明指定了内部链接或外部链接,则后面声明处的标识符链接与先前声明中指定的链接相同。如果没有先前声明可见,或者先前声明未指定链接,则标识符具有外部链接。
在声明extern char x
,第一个声明x
是不可见的,因为它已被第二个声明隐藏。因此,它不符合"该标识符的事先声明是可见的"的条件。第二次x
声明是可见的,因此就上款而言,它是"事先声明"。
那么最后一句应该控制:前面的声明指定没有链接(6.2.2 6,没有extern
的块范围标识符没有链接(,所以第三个x有外部链接。
则违反 6.2.2 7,因为第一个x
具有内部链接,第三个x
具有外部链接:
如果在翻译单元中,同时出现具有内部和外部链接的相同标识符,则行为未定义。
由于不违反语法规则或约束,因此标准不需要 C 实现来报告诊断。由于行为是未定义的,它可以做任何事情,包括接受此代码并使第三个x
引用与第一个x
相同的对象。因此,Clang和GCC的行为都没有违反这方面的标准。但是,由于违反了 6.2.2 7,因此可能首选诊断,并且不存在诊断可以被视为 Clang 的缺陷。
(感谢Paul Ogilvie和T.C.用他们的评论告知我对此的想法。