GCC 不会抱怨重新定义外部变量?

GCC does not complain about re-definition of external variable?

这个简单的代码(MCVE):

#include <stdio.h>

int a = 3;
int main(){
    printf("%d\n", a);
    return 0;
}
int a; // This line

令我惊讶的是,GCC(MinGW GCC 4.8.2、4.9.2 和 6.3.0)没有给出任何错误,甚至没有关于标记行的警告!但是,如果我在其第二个定义中为 a 赋值,它就会执行。

更奇怪的是,g++告诉我第二次重新定义是错误的,但是gcc却没有。

因为没有关键字extern,不应该是重新定义一个已经存在的变量吗?

来自 C 标准(6.9.2 外部对象定义)

1 If the declaration of an identifier for an object has file scope and an initializer, the declaration is an external definition for the identifier.

2 A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.

并且在 C 标准中有一个例子

int i1 = 1; // definition, external linkage
//...
int i1; // valid tentative definition, refers to previous

所以在你的程序中这个声明

int a = 3;

是标识符的外部定义a

还有这个

int a;

是暂定定义,参考了之前标识符的外部定义

如果在第二个声明中使用初始值设定项,那么您将获得标识符的两个外部定义,并且编译器将发出错误,因为只能存在一个外部定义。

考虑到 C 和 C++ 相对于此上下文的不同,

来自 C++ 标准(C.1.2 条款 6:基本概念)

6.1

Change: C++ does not have “tentative definitions” as in C. E.g., at file scope,

int i;
int i;

is valid in C, invalid in C++.

在C中叫做暂定

Cppreference 说:

Tentative definitions

A tentative definition is an external declaration without an initializer, and either without a storage-class specifier or with the specifier static.

A tentative definition is a declaration that may or may not act as a definition. If an actual external definition is found earlier or later in the same translation unit, then the tentative definition just acts as a declaration.

[...]

int i3; // tentative definition, external linkage

int i3; // tentative definition, external linkage 

extern int i3; // declaration, external linkage