在作用域内与作用域外定义extern变量的代码是否为well-defined?
关于extern变量重复定义的代码合法性问题
这个问题问到点子上了——这段代码不是well-defined的,属于C标准里的未定义行为,原因和extern的用法以及单定义规则(One Definition Rule,ODR)有关,咱们一步步拆解:
先看你的代码:a.c
int a; void foo(void) { a = 3; }
b.c
extern int a = 10; /*same as "extern a; int a = 10?" */ void foo(void); int main(void) { foo(); printf("%d", a); }
关键规则拆解
首先要明确C语言里的一个核心规则:带有初始化器的extern声明,本质上是一个变量定义。也就是说extern int a = 10;完全等价于int a = 10;——这里的extern关键字因为初始化器的存在失去了“引用声明”的作用,反而直接变成了一个定义(分配存储空间并设置初始值)。
再看a.c里的int a;:这是一个暂定定义(tentative definition),在编译链接阶段,它会被视为一个完整的全局变量定义,初始值默认为0(全局变量默认自动初始化为0)。
问题出在哪?
你在两个不同的编译单元(a.c和b.c)里都定义了同一个全局变量a:一个是暂定定义,一个是带初始化的显式定义。这直接违反了C标准的单定义规则:全局变量在整个程序中只能有一个合法定义。
这种情况下,程序的行为是未定义的:
- 部分编译器/链接器会直接报错,提示
multiple definition of 'a',拒绝生成可执行文件; - 有些链接器可能会默默合并两个定义,比如优先使用带初始化的版本(也就是b.c里的10),然后
foo()把a改成3,最终输出3; - 但你绝对不能依赖这种行为,因为C标准没有规定链接器必须这么处理,不同工具链的表现可能完全不同,甚至在优化级别变化时都会出现意外结果。
关于你注释里的疑问
你问extern int a = 10;是不是等价于extern a; int a = 10;——答案是完全不等价:
extern a; int a = 10;是先做一个引用性声明(告诉编译器“a在其他地方已经定义了”),然后再定义a;- 而
extern int a = 10;直接就是一个定义,没有中间的引用声明步骤,本质和int a = 10;一模一样。
总结:这段代码不符合C标准的规定,属于未定义行为,不能被认为是well-defined的。
内容的提问来源于stack exchange,提问作者doraemon1
相关产品推荐
相关产品推荐

