来自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.c
和b.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编译器项目(课程作业)。所以对我来说可能很重要。尽管家庭作业没有达到这个目的,但我很好奇,想要了解更多。谢谢!
答案 0 :(得分:2)
它被称为暂定定义
具有文件范围的对象的标识符声明 没有初始化程序,没有存储类说明符或者没有 存储类说明符静态,构成一个暂定的 定义。如果翻译单元包含一个或多个暂定单元 标识符的定义,翻译单元包含否 该标识符的外部定义,然后行为是完全正确的 好像翻译单元包含一个文件范围声明 标识符,在翻译结束时使用复合类型 单位,初始化程序等于0。
因此任何具有这种暂定定义的编译单元(.o文件)都会实现该对象。将两个这样的单元链接在一起具有未定义的行为,您通常应该遇到“多重定义的符号”错误。一些编译器/链接器只是这样做,你必须确保这些符号具有相同的大小和类型。