I know some basic usage of extern, but one thing really bothers me.
The following picture is what I found in the specification.

img1 img2

如果真的如图所示,为什么下面两段代码会不同呢?

#include <stdio.h>

int i;

int main() {
    printf("i = %d\n", i);
    return 0;
}
#include <stdio.h>

extern int i;

int main() {
    printf("i = %d\n", i);
    return 0;
}

此外,以下代码将显示警告:

警告:‘I’已初始化并声明为‘extern’

为什么?

extern int i = 0;

有没有人能告诉我该死的extern是怎么回事?

推荐答案

图像中的文本是错误的,因为"外部"和"自动"并不像它所说的那样是应用于声明的默认说明符.extern的效果取决于声明出现在哪里以及之前有哪些声明.文件范围内的声明int i;不能说具有默认的extern说明符,因为extern int i;具有不同的效果,如下所述.

在文件范围内,int i;tentative definition,用于名为i的对象(C 2018 6.9.2 2).尽管它的名字是tentative definition,但它并不是一个定义(就像面试中的潜在员工不是员工一样).如果翻译单元中没有常规定义,则临时定义将导致创建定义.然而,由于不同的C实现对int i;的处理不同,C标准没有定义如果有多个定义会发生什么(C 2018 6.9 5,C 2018 4 2).一些C实现将多个由兼容的试验性定义产生的定义合并为单个定义.有些人认为这是一个错误.The default changed in GCC version 10.

在文件范围内,extern int i;是名为i的对象的declaration,该对象不是定义.它不为i保留存储空间,如果在程序中使用i,则必须在程序的其他地方为其定义.这个声明给i外部链接unless一个先前的声明是可见的(见下文).

在文件范围内,extern int i = 0;是名为i的对象的definition.根据C标准的规则,带有初始化的声明是一个定义,即使它有extern个,所以编译器应该将其视为一个定义.然而,C用户的惯例是不将extern与定义一起使用.因此,使用extern声明int i,并使用= 0提供初始化,这违反了该约定.但这只是用户的惯例,而不是C标准的规则.因此,编译器会警告这种违反约定的行为,但符合C标准的编译器必须接受这段代码作为i的定义.(如果您将警告转换为错误,就像使用Clang或GCC的-Werrorswitch 一样,编译器不接受此代码,并成为不符合标准的编译器.)

作为另一个复杂情况的例子,如果出现extern int i;,其中可以看到先前将i声明为static int i;,则iextern int i;处的链接是内部的,而不是外部的(C 2018 6.2.2 4).extern的规则不能被声明为"它使链接位于外部",甚至不能被声明为"在文件范围内,它使链接位于外部".它的效果既取决于它的范围,也取决于之前的声明,使用C标准中不同位置声明的多个规则.

C++相关问答推荐

如何将不同长度的位转换成字节数组?

在一个小型玩具项目中实现终端历史记录功能

ARM64 ASIMD固有的加载uint8_t* 到uint16x8(x3)?

C语言中的strstr问题

仅在给定的大小和对齐方式下正确创建全局

在每种If-Else情况下执行语句的最佳方式

防止规范模式在C++中 echo 特殊字符

在Linux上使用vscode和lldb调试用Makefile编译的c代码

在另一个函数中使用realloc和指针指向指针

Go和C中的数据 struct 对齐差异

C中的回文数字

在运行时判断C/C++指针是否指向只读内存(在Linux操作系统中)

是否有单独的缓冲区用于读写库调用?

无算术运算符和循环的二进制乘法

为什么一个在线编译器拒绝这个VLA代码,而本地的Apple clang却不拒绝;t?

C: NULL>;NULL总是false?

#define X Defined(Y) 是有效的 C/C++ 宏定义吗?

多行表达式:C 编译器如何处理换行符?

如何正确探测平台设备?

char* 上的 free() 被 valgrind 识别为无效