如果您有两个使用相同结构A的翻译单元,通常将该结构放在标题中,即。即让预处理器将定义粘贴到两个TU中。
所以让我们说不要那样做。我们是否允许使用不同的定义?为什么呢?
createA.c:
typedef struct {
int data;
} A;
A createA() {
A a = {10};
return a;
}
main.c中:
#include <stdio.h>
typedef struct {
int value;
float x;
} A;
A createA();
int main(int argc, char *argv[])
{
A a = createA();
printf("value is %d, x is %f\n", a.value, a.x);
a.x = 3.1f;
printf("value is %d, x is %f\n", a.value, a.x);
}
尽管两个结构的大小不同,但VS 10似乎很乐意将它们联系在一起。这意味着对createA
的调用在两个TU(不同的堆栈修改)中的工作方式不同。这有点奇怪。当我向函数添加参数时,程序在运行时崩溃,但仍然没有链接器错误。
那么,为什么这很有趣?我想知道是否不可能将此机制用于Pimpl变体,而不是impl* impl_data
,您将在公共标头中使用unsigned char impl_data[impl_size]
之类的东西,并在实现中强制转换为实际的impl结构文件。除了实际考虑因素(例如确定impl_size),这是允许的吗?
答案 0 :(得分:4)
链接器不知道结构是如何定义的。甚至存在结构。所有链接器都关心的是使用外部链接解析名称,而结构是编译时的伪像。
链接器也不了解函数原型。因此,它不知道在某个翻译单元中调用的函数的参数是否实际上与编译到不同翻译单元中的函数所期望的参数相对应。所有链接器都会确保调用调用具有该名称的函数。
因此肯定允许链接器忽略许多错误。但这并不能使错误正确。他们仍然是错误。作为程序员,您有责任确保程序正确,并且一个翻译单元中的结构定义和原型与另一个翻译单元中的结构定义和原型兼容。 C不会充当你的守护天使。
这可能看起来不是完全用户友好的,并且在很多方面它都不是。有些语言旨在检测这样的错误。但是C没有那种哲学。
关于在一个翻译单元中使用struct s { char _[sizeof_struct_s]; };
和在另一个翻译单元中使用真实struct s
的具体示例,结果是未定义行为,不需要任何错误消息。所以它不应该用在符合要求的便携式代码中。尽管如此,如果您可以保证字符数组的正确对齐,并且您可以正确地计算大小(这将是一个很大的可维护性问题),它可能适用于许多体系结构。当然,没有任何保证,如果它打破了,你就可以保留它们。
答案 1 :(得分:1)
如果两个翻译单元使用相同结构类型的不同声明,那很好,实际上并不罕见。
如果在具有不同签名的两个翻译单元中定义外部符号(其中具有不同定义的参数类型的相同签名是不同的签名),则行为未定义。
请注意,这样,程序的两个部分可以具有相同结构类型的不同声明,但是它们无法找出这种差异,因为这两个定义永远不会在不创建未定义的情况下彼此接触行为。
这会回答你的问题吗?
答案 2 :(得分:1)
链接器并非真正涉及此处。如果您有一个功能f()
在一个翻译单元中定义并从另一个翻译单元中调用,并且如果它在两个翻译单元中以不同类型声明,则表示该功能{};} s(a)一个非常严重的问题,(b)不是传统的连接器(传统的&#34;传统的&#34;我的意思是相对简单的C被设计的)可以检测到的。具有定义函数的结构类型的不同声明与其被调用的地方的声明实际上不同于定义在一个转换单元中返回double但随后声明并调用它的函数,就好像它返回一样另一个是int。 (我的观点是,你不会倾向于从那里的链接器那里得到投诉。)