2012-12-31 86 views
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

有人能解释其中我已经错了吗?

+0

为什么你不这样做在'int a'的声明中,就像'int a = f.a;'一样?你正在“访问结构”,好像它是分配内存一样,但显然你不是! – t0mm13b

+0

@ t0mm13b:目的 - 学习.. – Jack

回答

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来避免手动进行数学运算。

+0

不是'offsetof'非标准GCC扩展? – Dolda2000

+2

@ Dolda2000不,这是ANSI C:见7.17.3 [这里](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1124.pdf); – dasblinkenlight

+0

你确定吗?即使现在看到GCC手册,它也会在其C扩展名下列出'offsetof'。当然,之后它可能已经在标准中被定义。 – Dolda2000

1

你可以做&f.b,并跳过实际的指针数学。

这是我如何会改变你的int b行:

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

BTW,当我运行程序的是,我得到2 ..0作为输出。

+0

它是UB,也许? – Jack

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)); 
+0

'((int)&f + sizeof(int))'将对具有sizeof(int *)> sizeof(int)'的系统造成严重破坏。除非编译器在成员之间添加填充,否则'(int *)&f + 1'或'(uintptr_t)&f + sizeof(int)'将起作用。 –

2

你的问题是&f + sizeof(int)。如果A是一个指针,并且B是一个整数,则在C中,A + B不意味着A加上B字节。相反,这意味着AB元素,其中元素的大小由指针类型A定义。因此,如果sizeof(int)在您的体系结构上为4,那么&f + sizeof(int)意味着“四个foo_t s到&f或4 * 8 = 32个字节到&f”。

尝试((char *)&f) + sizeof(int)

或者,当然,&f.a&f.b相反,很简单。后者不仅可以给你提供方便的指针,而且可以帮助你减轻所有这些表演的负担,同时也是明确的,可以理解的。 :)

2

表达式&f + sizeof(int)将数字添加到类型为foo_t*的指针。指针算术总是假定指针指向数组的一个元素,并且该数字被视为元素的数量。

所以,如果sizeof(int)是4,&f + sizeof(int)点中的四个foo_t结构过去f,或&f4*sizeof(foo_t)字节。

如果必须使用字节数,这样的事情可能工作:

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

...假设有成员ab之间没有填充。

但是,有没有什么原因,你不只是得到的价值f.b或指针&f.b