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

标签 c compiler-construction declaration linkage

来自 C99 标准 6.2.3:

If the declaration of an identifier of an object has file scope and no storage-class specifier, its linkage is external.

和6.7

A declaration specifies the interpretation and attributes of a set of identifiers. A definition of an identifier is a declaration for that identifier that:

— 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 在文件末尾完成。从上面的两个例子,我们可以推断出外部声明是在文件范围的末尾检查的。 (仍然不知道标准在哪里说的)

但是,数组声明是异常(exception)的。修改后的代码不再有效:

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 编译器项目(类(class)作业)。所以弄清楚它对我来说可能很重要。虽然作业没有到这一步,但我很好奇,想了解更多。谢谢!

最佳答案

这叫做暂定

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.

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

关于C - 在什么情况下外部声明成为定义?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22928879/

相关文章:

compiler-construction - LLVM 抖动可以在连续内存地址中发出 native 代码吗?

C : differences between prototype declaration in header and function declaration for implementation?

Ruby 术语问题 : Is this a Ruby declaration, 定义和赋值,同时进行?

c - C 上的堆栈(简单程序)

c - N 个指向函数的指针的数组返回指向函数的指针

c - 强制 scons 中出现 "undefined symbol"编译时错误

c++ - 要下载什么编译器,如何下载编译器,如何将编译器添加到Qt中? (Ubuntu 12.04)

c# - Visual Studio编译时跳过某些错误

java - 括号和数组声明的问题

c - 使用 While 循环查找数字的最小除数