【问题标题】:GCC does not complain about re-definition of external variable? [duplicate]GCC 不抱怨外部变量的重新定义? [复制]
【发布时间】:2018-04-27 13:48:56
【问题描述】:

这个简单的代码(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而重新定义现有变量吗?

【问题讨论】:

  • 未初始化的全局变量隐式为extern
  • 如果两者都有初始化程序,它会抱怨。写成这样要求不要抱怨。二是暂定定义。你通常会先看到未初始化的声明,但写的没问题。
  • 如果未初始化的全局变量隐含为extern,则意味着int a; ... int a; 将等同于extern int a; ... extern int a;,但后者不链接。

标签: c gcc redefinition


【解决方案1】:

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

1 如果对象标识符的声明具有文件范围并且 一个初始化器,声明是一个外部定义 标识符

2 具有文件范围的对象的标识符声明 没有初始化器,也没有存储类说明符或 存储类说明符静态,构成一个暂定 定义。如果一个翻译单元包含一个或多个暂定 标识符的定义,并且翻译单元不包含 该标识符的外部定义,则行为正是 好像翻译单元包含该文件的文件范围声明 标识符,与翻译结束时的复合类型 单位,初始化器等于 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

更改:C++ 没有 C 中的“暂定定义”。例如,在 文件范围,

int i;
int i;

在 C 中有效,在 C++ 中无效。

【讨论】:

  • 那么第二个int a;可以写成static int a;,效果一样(隐式extern)?
  • @iBug 可能没有用 static 关键字写,因为它已经有外部定义了。同一个标识符在同一个翻译单元中可能没有外部和内部链接。
  • 值得注意的 C11 未来语言方向:“在文件范围内声明具有内部链接的标识符而没有静态存储类说明符是一个过时的功能。”
【解决方案2】:

在 C 中称为暂定定义

Cppreference 说的:

暂定定义

暂定定义是没有 初始化器,并且没有存储类说明符或带有 说明符是静态的。

暂定定义是一种声明,可能会或可能不会充当 定义。如果更早或更晚找到实际的外部定义 在同一个翻译单元中,暂定定义才起作用 作为声明。

[...]

int i3; // tentative definition, external linkage

int i3; // tentative definition, external linkage 

extern int i3; // declaration, external linkage

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-31
    • 1970-01-01
    • 2011-07-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多