参考以下代码:
#include <stdio.h>
int a;
int a;
int main()
{
int b;
int b;
return 0;
}
为什么编译器(GCC)会抱怨重新声明只有变量&#39; b&#39;而不是&#39;?
redef.c:在功能&#39; main&#39;: redef.c:19:错误:重新声明&#39; b&#39;没有联系
redef.c:18:错误:先前声明&#39; b&#39;在这里
答案 0 :(得分:8)
这是因为a
有外部联系和标准状态(C11,6.2.2 / 2):
可以通过名为linkage的进程引用在不同作用域或同一作用域中多次声明的标识符来引用相同的对象或函数。有三种联系:外部,内部和无。
在构成整个程序的翻译单元和库集合中,具有外部链接的特定标识符的每个声明表示相同的对象或函数。在一个翻译单元内,具有内部链接的标识符的每个声明表示相同的对象或功能。 没有链接的标识符的每个声明都表示一个唯一的实体。
因此,因为a
具有外部链接,所以这两个声明都引用相同的基础变量。由于b
没有链接,因此声明引用唯一变量,因此相互冲突。
答案 1 :(得分:1)
引用C99标准§6.9.2¶2
具有文件范围的对象的标识符声明 没有初始化程序,没有存储类说明符或没有 存储类说明符静态,构成一个暂定的 定义。如果翻译单元包含一个或多个暂定 标识符的定义,翻译单元包含否 该标识符的外部定义,然后行为是完全正确的 好像翻译单元包含一个文件范围声明 标识符,在翻译结束时使用复合类型 单位,初始化程序等于0。
因此,两个陈述
int a;
int a;
构成暂定的定义。根据上面引用的部分,行为就好像
取代了两个陈述int a = 0;
但是,b
中定义的main
是一个自动变量,即它具有自动存储分配。自动变量不能有两种定义。
答案 2 :(得分:0)
int a; int a;
是一个暂定定义。从6.9.2p2开始:
具有没有初始化程序的文件范围的对象的标识符声明,以及 没有存储类说明符或存储类说明符静态,构成一个 暂定定义。如果翻译单元包含一个或多个临时定义 然后,标识符和转换单元不包含该标识符的外部定义 行为就像翻译单元包含该文件范围声明一样 标识符,具有复制类型,如翻译单元的末尾,带有初始化程序 等于0。
暂定定义仅允许在文件范围内。
int b; int b;
非法的原因是因为6.7p3:
如果标识符没有链接,则标识符的声明不得超过一个 (在声明符或类型说明符中)具有相同的作用域和相同的名称空间
在函数中声明的标识符而不是static
或extern
没有链接,这在6.2.2p6中有描述
以下标识符没有链接:声明为除。之外的任何标识符 对象或功能;声明为函数参数的标识符;块范围 没有存储类说明符extern声明的对象的标识符。