定义和声明应该匹配吗?

在我的.h文件中我有

extern int a[4]; 

在我的.c文件中

 int a[10]; 

这有什么问题吗?

声明和定义大小很重要? 不对?

如果我在其中一个文件中写入sizeof(a) ,那么输出是什么? 这是未定义的行为吗?

如果在源文件中包含头文件,则a的两个声明必须与C所说的类型相同

(C11,6.7p4)“同一范围内涉及同一对象或function的所有声明均应指定兼容类型。”

即使两个声明都在两个翻译单元中,它们也需要具有相同的类型:

(C11,6.2.7p2)“所有引用相同对象或函数的声明都应具有兼容类型;否则,行为未定义。”

看起来像这样:

 extern int a[4]; int a[10]; int main() { return 0; } 

gcc报告a的冲突类型:

 cc -Wall -g -ggdb -pipe -pedantic -std=gnu99 test.c -o test test.c:2:5: error: conflicting types for 'a' int a[10]; ^ test.c:1:12: note: previous declaration of 'a' was here extern int a[4]; ^ 

正如@ouah所说,它是正式的未定义行为,并且容易出错, 因此它应该永远不会存在于生产代码中

但它会被正确的结果所接受,但大多数(如果不是全部)常见编译器(gcc,clang,msvc)

如果包含包含extern int a[4];的.h文件extern int a[4]; 在.c中包含int a[10]; 你会得到一个错误,因为你正在重新定义一个不同的类型(正如其他人已经说过的那样)。

如果只在其他编译单元中包含.h,则链接器应忽略大小并正确链接。

只需在.c中定义sizeof(a) == 4 * sizeof(int) ,其中包含.declaring的其他编译单元中的sizeof(a) == 4 * sizeof(int)

工作范例:

foo.c:

 #include  int a[10]; void display(); int main() { for(int i=0; i 

foo2.c:

 #include  extern int a[4]; void display() { printf("sizeof(a)=%d\n", sizeof(a)); for(int i=0; i 

编译+链接: cc foo.c foo2.c -o foo :甚至没有警告

执行:

 sizeof(a)=40 sizeof(a)=16 0 1 2 3 

这通常用于fortran中的公共区域,其中编译单元只能声明一个共同的开头,但我无法想象C中这样一个恐怖的真实用例。


它起作用的原因

编译器在编译时无法检测到同一程序中存在不兼容类型的声明, 因为它们位于不同的转换单元中,因此处理但编译阶段不同 - 可能在不同的时间。

在链接时,链接器只能看到a的不同声明的地址,并确保所有.o(或.obj)都获得相同的地址。 在不破坏多语言兼容性的情况下,很难做到不同:它是在C模块和汇编语言之间共享数组的方式。

为什么你不应该使用它

你可以说,当面对标准定义为未定义的行为时,没有什么能阻止编译器执行写操作。 但Hans Passant曾经给过我一篇关于未来编译器研究的文章的链接。 以下是一些摘录:

本文是关于C抽象机器的一种新的内存安全解释,它提供了更强大的保护,有利于安全性和调试...... [作者]certificateC的内存安全实现不仅可以支持C抽象指定的机器,但更广泛的解释仍然与现有代码兼容。 通过在硬件中强制执行模型,我们的实现提供了可用于为C ...提供高级安全属性的内存安全性。

[实现]内存function表示为三元组(基本,绑定,权限),它松散地打包成256位值。 这里base为虚拟地址区域提供了一个偏移量,并限制了访问区域的大小......特殊function加载和存储指令允许将function溢出到堆栈或存储在数据结构中,就像指针一样...警告说不允许指针减法。

添加权限允许function成为授予引用内存的某些权限的令牌。 例如,内存function可能具有读取数据和function的权限,但不能写入它们(或只是写入数据而不是写入数据)。 尝试任何不允许的操作都会导致陷阱。

[结果]证实,可以保留function系统内存模型的强语义(提供不可绕过的内存保护),而不会牺牲低级语言的优势。

(强调我的)

TL / DR:没有什么能阻止未来的编译器在对象(已编译)模块中添加数组的大小信息,如果它们不兼容则会引发错误。 目前存在对这些特征的研究