您能帮忙解释一下为什么以下程序正确打印了所有结构成员的值吗?
struct st
{
int i;
char c1;
int j;
char c2;
};
int main()
{
struct st a = {5, 'i', 11, 'H'};
struct st * pa = &a;
int first;
char second;
int third;
char fourth;
first = *((int*)pa);
second = *((char*)pa + 4); /* offset = 4 bytes = sizeof(int) */
third = *((int*)pa + 2); /* why (pa + 2) here? */
fourth = *((char*)pa + 12); /* why (pa + 12) here? */
printf ("first = %d, second = %c, third = %d, fourth = %c\n", first, second, third, fourth);
return 0;
}
输出:第一= 5,第二= i,第三= 11,第四= H
如何使以上程序泛化?
答案 0 :(得分:1)
那是因为在结构中添加了填充字节。 char second;
之后将添加三个填充字节,这是因为char
之后是int
(具有较大对齐方式的成员),因此将插入填充字节以使较大成员的对齐。
答案 1 :(得分:1)
如何使以上程序泛化?
使其可靠运行的唯一方法是不猜测偏移量。使用标准的offsetof宏,并始终使用字符指针进行指针算术:
first = *(int*)((char*)pa + offsetof(struct st, i));
您不必在进行访问时就为该字段命名,但是如果您打算将其传递给函数,则绝对应该使用该宏来计算该违约。
答案 2 :(得分:0)
这是因为structure
的填充。
填充后,structure
将如下所示。
struct st
{
int i;
char c1;
char padding[3]; // for alignment of j.
int j;
char c2;
char padding[3]; // for alignment of structure.
};
因此
first = *((int*)pa);
second = *((char*)pa + 4); /* offset = 4 bytes = sizeof(int) */
third = *((int*)pa + 2); /* offset = 8 bytes(pointer arithmetic) to point to int j*/
fourth = *((char*)pa + 12); /* offset = 12 bytes to point to char c2*/
有关结构填充的更多信息,请阅读 stackblitz
答案 3 :(得分:0)
与其他答案一样-padding
。
但是有些编译器允许您打包结构,以消除(在大多数情况下)填充。
gcc:
struct __attribute__((packed)) st
{
....
}
访问打包结构的代码可能效率较低且更长。
答案 4 :(得分:-1)
创建结构时,所有变量都占用相同的空间量(32位),其余未使用的位将被填充。因此,即使您在结构中定义char
,也将占用4个字节。
这是由于您的处理器以32位来寻址数据这一事实,即使之后使用的位更少。另一端的内存为每个地址存储1个字节,但是当CPU提取数据时,数据将适应总线架构(取决于处理器)。
还请注意,偏移量取决于您使用的指针。在这种情况下,char*
将增加1,而int*
将增加4。
这也意味着代码不可移植,因为例如int
可能在不同的体系结构上定义的大小不相同。