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

What is the rationale behind tentative definitions in C?

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

#include <stdio.h>
int s=5;
int s;
int main(void)
{
     printf("%d",s);
}

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

暂定定义是任何没有存储 class 说明符和初始值设定项的外部数据声明。

C99 6.9.2/2

A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.

我的问题是,允许暂定定义的理由是什么?在C中有什么用吗?为什么 C 允许临时定义?

这是一个有用的例子:

void (*a)();

void bar();
void foo()
{
    a = bar;
}

static void (*a)() = foo;

/* ... code that uses a ... */

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

创建暂定定义是为了桥接 C89 之前存在的不兼容模型。这在 C99 rationale 部分 6.9.2 外部对象定义中包含:

Prior to C90, implementations varied widely with regard to forward referencing identifiers with internal linkage (see §6.2.2). The C89 committee invented the concept of tentative definition to handle this situation. A tentative definition is a declaration that may or may not act as a definition: If an actual definition is found later in the translation unit, then the tentative definition just acts as a declaration. If not, then the tentative definition acts as an actual definition. For the sake of consistency, the same rules apply to identifiers with external linkage, although they're not strictly necessary.

和 C99 原理中的 6.2.2 部分说:

The definition model to be used for objects with external linkage was a major C89 standardization issue. The basic problem was to decide which declarations of an object define storage for the object, and which merely reference an existing object. A related problem was whether multiple definitions of storage are allowed, or only one is acceptable. Pre-C89 implementations exhibit at least four different models, listed here in order of increasing restrictiveness: