定义和声明应该匹配吗?
在我的.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:没有什么能阻止未来的编译器在对象(已编译)模块中添加数组的大小信息,如果它们不兼容则会引发错误。 目前存在对这些特征的研究