使用基址访问结构成员

时间:2018-10-03 09:16:00

标签: c pointers

您能帮忙解释一下为什么以下程序正确打印了所有结构成员的值吗?

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

如何使以上程序泛化?

5 个答案:

答案 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可能在不同的体系结构上定义的大小不相同。