C - 外部声明在什么情况下成为定义?

时间:2014-04-08 06:09:51

标签: c compiler-construction declaration linkage

来自C99标准6.2.3:

  

如果对象标识符的声明具有文件范围而没有存储类说明符,则其链接是外部的。

和6.7

  

声明指定一组标识符的解释和属性。标识符的定义是该标识符的声明:

— for an object, causes storage to be reserved for that object;
— for a function, includes the function body;99)
— for an enumeration constant or typedef name, is the (only) declaration of the identifier.

不幸的是,我还没有找到关于编译器何时将外部声明视为定义(这意味着类型必须完整并且计算存储大小)的进一步描述。

所以我做了一些实验。首先我注意到了:

struct A a;
int main() {
}

无效,gcc说类型A不完整,并且不知道如何为a分配存储空间。 但是,有趣的是,我们有以下有效代码:

struct A a;
int main() {
}
struct A {int x;};

这也是合理的,因为类型A在文件末尾完成。从上面的两个例子中,我们可以推断出在文件范围的末尾检查了外部声明。 (仍然不知道标准对此有何评价)

但是,数组声明是例外。修改后的代码不再有效:

struct A a[1];
int main() {
}
struct A {int x;};

C99标准确实谈到了这一点,它说数组的元素必须是完整的类型。所以问题出现了:struct A a[1]定义或声明是什么?不要急于回答。请查看以下示例。

此处我们有两个文件:a.cb.c。在a.c

#include <stdio.h>
int arr[10];
void a_arr_info() {
    printf("%lu at %lx\n", sizeof arr, (size_t)arr);
}
b.c

#include <stdio.h>
int arr[20];
void b_arr_info() {
    printf("%lu at %lx\n", sizeof arr, (size_t)arr);
}
int main() {
    a_arr_info();
    b_arr_info();
}

结果令人惊讶。输出显示两个文件中的arr引用相同的地址。这可以理解,因为arr都在文件范围内,因此它们是外部链接。问题是,它们有不同的大小。在什么文件中,编译器将声明作为定义并分配内存?

为什么我这样问?因为,嗯,我正在研究一个简化的C编译器项目(课程作业)。所以对我来说可能很重要。尽管家庭作业没有达到这个目的,但我很好奇,想要了解更多。谢谢!

1 个答案:

答案 0 :(得分:2)

它被称为暂定定义

  

具有文件范围的对象的标识符声明   没有初始化程序,没有存储类说明符或者没有   存储类说明符静态,构成一个暂定的   定义。如果翻译单元包含一个或多个暂定单元   标识符的定义,翻译单元包含否   该标识符的外部定义,然后行为是完全正确的   好像翻译单元包含一个文件范围声明   标识符,在翻译结束时使用复合类型   单位,初始化程序等于0。

因此任何具有这种暂定定义的编译单元(.o文件)都会实现该对象。将两个这样的单元链接在一起具有未定义的行为,您通常应该遇到“多重定义的符号”错误。一些编译器/链接器只是这样做,你必须确保这些符号具有相同的大小和类型。