C中暂定定义背后的基本原理是什么?

考虑以下程序。 这会给出任何编译错误吗?

#include  int s=5; int s; int main(void) { printf("%d",s); } 

乍一看似乎编译器会给出变量重定义错误,但程序根据C标准完全有效。 (请参阅http://ideone.com/Xyo5SY上的现场演示)。

暂定定义是没有存储类说明符且没有初始化程序的任何外部数据声明。

C99 6.9.2 / 2

对于具有文件范围而没有初始化程序且没有存储类指定程序或存储类指定静态的对象的标识声明构成了一个暂定的定义。 如果翻译单元包含一个或多个用于标识符的暂定定义,并且翻译单元不包含该标识符的外部定义,则该行为就像翻译单元包含该标识符的文件范围声明一样,其复合类型为翻译单元的结尾,初始化程序等于0。

我的问题是,允许暂定定义的理由是什么? 在C中有没有用过这个? 为什么C允许暂定?

创建了暂定定义,作为桥接C89之前存在的不兼容模型的一种方法。 这在C99基本原理部分6.9.2外部对象定义中有所介绍:

在C90之前,关于具有内部链接的前向引用标识符的实现差别很大(参见§6.2.2)。 C89委员会发明了暂定定义的概念来处理这种情况。 暂定定义是可以作为定义或不作为定义的声明:如果稍后在翻译单元中找到实际定义,则暂定定义仅作为声明。 如果不是,则暂定定义作为实际定义。 为了保持一致性,相同的规则适用于具有外部链接的标识符,尽管它们并非绝对必要。

C99基本原理的第6.2.2节说:

用于具有外部链接的对象的定义模型是C89的主要标准化问题。 基本问题是决定对象的哪些声明为对象定义存储,哪些只引用现有对象。 一个相关的问题是,是否允许存储的多个定义,或者只有一个是可接受的。 预C89实现至少展示了四种不同的模型 ,按照限制性增加的顺序列出:

这是一个有用的案例:

 void (*a)(); void bar(); void foo() { a = bar; } static void (*a)() = foo; /* ... code that uses a ... */ 

关键点在于foo的定义必须引用a ,而a的定义必须引用foo 。 具有初始化结构的类似示例也应该是可能的。