当传递指向结构的指针时,接收它的函数可能会尝试访问其所有字段中的任何一个。
如果您收到一个struct something *
,您希望您可以读取sizeof(struct something)
您收到的指针后面的任何字节。因此,不自己保留这些字节struct
会使它们不兼容 - 每当函数尝试访问您尚未分配的字节时,它将访问非保留内存,因此它可能是分段错误,或者可能会破坏另一个的结构数据。
看看这个程序:
#include <stdlib.h>
#include <stdio.h>
struct __attribute__ ((__packed__)) pair {
short first;
char second;
long int third;
int forth;
char last;
};
void main(void) {
struct pair myPair;
printf("myPair is at 0x%x\n", &myPair);
printf("myPair.first is at 0x%x\n", &(myPair.first));
printf("myPair.second is at 0x%x\n", &(myPair.second));
printf("myPair.third is at 0x%x\n", &(myPair.third));
printf("myPair.forth is at 0x%x\n", &(myPair.forth));
printf("myPair.last is at 0x%x\n", &(myPair.last));
}
和一个示例输出:
myPair is at 0xabbd0aa0
myPair.first is at 0xabbd0aa0
myPair.second is at 0xabbd0aa2
myPair.third is at 0xabbd0aa3
myPair.forth is at 0xabbd0aab
myPair.last is at 0xabbd0aaf
我们在这里学到的是,每个字段都存储在内存中的前一个字段的旁边,更准确地说是sizeof(previous_field)
前一个字段右侧的字节(什么时候- 请参阅this以了解为什么要打包,但这是理想情况)。struct
packed
所以,想象一下我们想要创建另一个struct
与这个兼容。如果我们创建类似的东西:
struct __attribute__ ((__packed__)) small_pair {
long int first;
char second;
int third;
char forth;
};
我们可以通过强制转换将 a 传递struct small_pair *
给任何期望 a 的函数struct pair *
:
void my_function(struct pair *);
void main(void) {
struct small_pair my_small_pair;
// ...
my_function((struct pair*) &my_small_pair);
// ...
}
void my_function(struct pair *a_pair) {
//...
printf("Second character of pair is %c\n", a_pair->second);
//...
printf("Last character of pair is %c\n", a_pair->last);
//...
}
编译后,访问a_pair->second
是“读取结构开始后两个字节的一个字节”(0xabbd0aa2 - 0xabbd0aa0 = 2
)。所以这将是 a 字段的第三个字节first
,struct small_pair
无论它具有哪个值。
但是,怎么a_pair->last
办?它是0xf
结构开始后的 (15) 个字节,但显然超出了它的空间(sizeof(struct small_pair)
只有 14 个)。
所以这将取决于变量在内存中的加载方式,但显然我们不会引用我们想要的值。最好的情况是该地址超出我们的进程空间,因此我们得到一个分段错误,程序中止。但很可能在该内存位置声明了另一个变量,我们将读取/写入与我们想要的不同的变量,留给谁知道结果。
因此,如果我们只是在 的末尾添加另一个 2 字节长的字段struct small_pair
,我们保证每个可能的 a 引用struct pair
在我们自己的 中仍然是正确的struct
,因此它们将在内存级别兼容。
然后,它仍然保留了语义级别的兼容性,但这是另一回事 :)