通过地址访问结构体成员

4

考虑一个具有两个整数成员的结构体。 我想通过地址获取这两个成员。 我可以成功获取第一个成员,但是我在获取第二个成员时得到了错误的值。 我相信这是垃圾值。 这是我的代码:

#include <stdio.h>

typedef struct { int a; int b; } foo_t;

int main(int argc, char **argv)
{

  foo_t f;
  f.a = 2;
  f.b = 4;
  int a = ((int)(*(int*) &f));
  int b = ((int)(*(((int*)(&f + sizeof(int)))))); 
  printf("%d ..%d\n", a, b);
  return 0;
}

我得到的是:

2 ..1

有人可以解释一下我错在哪里吗?

为什么你在声明int a时不这样做,像这样:int a = f.a;?你似乎在“访问结构体”,就好像它是分配的内存,但很明显你并没有! - t0mm13b
@t0mm13b:目的 - 学习... - Jack
5个回答

7

根据C标准,第一个成员的偏移量必须始终为零; 这就是为什么你的第一个转换有效的原因。 然而,由于填充,结构体的第二个成员的偏移量可能不一定等于第一个成员的大小。

此外,将数字添加到指针并不会将字节数添加到地址:相反,它会添加所指向的东西的大小。 因此,当您将 sizeof(int) 添加到 &f 时,sizeof(int)*sizeof(foo_t) 被添加。

如果您想自己计算,请使用offsetof运算符,像这样:

int b = *((int*)(((char*)&f)+offsetof(foo_t, b)));

这个方法有效的原因是 sizeof(char) 总是一,这是C标准所要求的。
当然你也可以使用 &f.b 来避免手动计算。

1
offsetof不是GCC的非标准扩展吗?” - Dolda2000
2
@Dolda2000 不是,那是ANSI C:请参见此处的第7.17.3节(http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf); - Sergey Kalinichenko
你确定吗?即使现在我查看GCC手册,在其C语言扩展中列出了“offsetof”。当然,它可能是在标准之后定义的。 - Dolda2000
根据我之前评论中的链接,这至少是自2005年以来的标准,但我认为我在90年代末跨平台使用过它。不过可能最初是作为gcc扩展开始的。 - Sergey Kalinichenko

2
您的问题是&f + sizeof(int)。如果A是指针,B是整数,则在C中A + B并不意味着A加上B字节。而是意味着A加上B个元素,其中元素的大小由A的指针类型定义。因此,如果您的架构上sizeof(int)为4,则&f + sizeof(int)表示“从&f开始的四个foo_t,或者从&f开始的32个字节”。

请尝试改用((char *)&f) + sizeof(int)

当然,您也可以使用&f.a&f.b,这样不仅可以方便地获得int指针,还可以避免所有这些转换,并且易于理解 :)


2
表达式 &f + sizeof(int) 向类型为 foo_t* 的指针添加一个数字。指针算术运算始终假定指针指向数组的元素,而数字被视为元素的计数。
因此,如果 sizeof(int) 是 4,则 &f + sizeof(int) 指向比 f 多四个 foo_t 结构体,或者在 &f 之后 4*sizeof(foo_t) 字节。
如果您必须使用字节数,请尝试类似以下内容的语句:
int b = *(int*)((char*)(&f) + sizeof(int));

假设成员ab之间没有填充(padding)。

但是你为什么不直接获取值f.b或指针&f.b呢?


1

您可以使用&f.b,并跳过实际的指针数学计算。

这是我如何更改您的int b行:

int b = (int)(*(((int*)(&(f.b)))));

顺便提一下,当我按原样运行您的程序时,输出结果是2 ..0


1

这个有效:

  int b = ((int)(*(int*)((int)&f + sizeof(int))));

您正在将&f解释为指针,当您将4(int的大小)添加到它时,它会将其解释为添加4个指针,这是16或32字节,具体取决于32位还是64位架构。如果您将指针转换为int,则会正确地向其添加4个字节。

这是正在发生的事情的解释。我不确定您在做什么,但您肯定不应该像那样做。这可能会使您在对齐等方面遇到问题。确定结构元素的偏移量的安全方法是:

  printf("%d\n", &(((foo_t *)NULL)->b));

((int)&f + sizeof(int))会在sizeof(int*) > sizeof(int)的系统上造成混乱。(int*)&f + 1(uintptr_t)&f + sizeof(int)可以工作,除非编译器在成员之间添加填充。 - Daniel Fischer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接