关于C语言文件作用域无存储类说明符时仅作变量声明的疑问
根据C语言存储类说明符规则:
- 所有函数默认extern
- 文件作用域的对象默认extern
- 块作用域的对象默认auto
假设在file1.c中定义文件作用域变量:
int i = 0;
现在有个疑问:在另一文件的文件作用域中,如何同时满足(1)无存储类说明符且(2)仅为该变量的声明?因为不加extern时,int i;看起来是定义,会引发重复定义的编译/链接错误;只有extern int i;才是单纯的引用声明。这似乎和“文件作用域对象默认extern”的规则矛盾,无法理解文件作用域下怎么存在无存储类说明符的仅声明情况。
要理清这个问题,得先明确C语言里两个核心概念:暂定定义(tentative definition)和引用声明(reference declaration):
- 文件作用域中,不带存储类说明符且没有初始化器的变量声明(比如
int i;),属于暂定定义,而非单纯的引用声明。它的行为规则是:- 如果整个程序中没有其他地方对
i的显式定义(比如int i=0;),这个暂定定义会被自动转为初始化为0的定义。 - 如果程序中已有显式定义(比如
file1.c里的int i=0;),严格遵循C标准的情况下,多个编译单元中的暂定定义会引发重复定义错误——因为标准允许编译器将暂定定义视为正式定义,这就和已有的显式定义冲突了。
- 如果整个程序中没有其他地方对
再看cppreference提到的“文件作用域对象默认extern”:这里的“默认extern”指的是这类变量的存储期和链接属性——文件作用域无存储类说明符的变量,默认拥有外部链接(external linkage),和用extern声明的变量链接属性一致。但这并不意味着它是单纯的引用声明,暂定定义本质是一种“可能成为定义的声明”,和extern int i;这种明确的引用声明有本质区别。
回到你的问题:在另一文件的文件作用域中,无法同时满足“无存储类说明符”和“仅为该变量的声明”。因为无存储类说明符的文件作用域变量声明,要么是带初始化器的定义,要么是不带初始化器的暂定定义(可能成为定义),不存在单纯的引用声明。cppreference的规则并没有矛盾,它只是说明了存储类的默认属性,而暂定定义是声明的特殊类型,二者属于不同层面的规则。
如果要在另一文件中单纯声明i以引用file1.c中的定义,必须显式使用extern int i;,这是唯一符合C标准的方式。
内容的提问来源于stack exchange,提问作者Karl Gardner

