2011-01-06 100 views
5

当下面的程序没有崩溃时,我感到很惊讶。使用指针访问结构元素

typedef struct _x { 
    int a; 
    char b; 
    int c; 
} x; 

main() { 
    x *ptr = 0; 
    char *d = &ptr->b; 
} 

按我的理解->运营商具有较高的优先级高于&运营商。所以我期望程序在下面的语句中崩溃,当我们试图解引用NULL指针tr

char *d = &ptr->b; 

但是声明&ptr->b评估为有效地址。有人能解释我错在哪里吗?

+0

这在某种程度上类似于`offsetof`宏。 – ruslik 2011-01-06 09:46:27

回答

2

&ptr->b == sizeof(int),这意味着_x.a后内_x偏移的b(这是类型int的)相对于所述地址*((x*)0)4(32位体系结构的典型值)的偏移量保存在d指针内。您必须访问d才能获得seg-fault。

4

您的代码不会崩溃的原因是您实际上没有取消引用指针。请注意,表达

&ptr->b 

实际上并没有尝试加载的ptrptr->b内容。相反,它只是存储它在内存中的位置的地址。你最终得到的是指向ptr指向的对象的b字段的指针。这将是地址0之后的几个字节,因此,对刚创建的指针进行解引用会导致段错误。

2

计算一个地址不需要访问内存。 &ptr->b的意思是“给我ptr指向的结构的b字段的地址。”这样做不需要查看可能存储在该内存位置的任何内容。

考虑索引数组而不是结构可能会有帮助。 C定义ptr[5]等同于*(ptr + 5),这意味着&(ptr[5])&(*(ptr + 5))相同。现在可以很容易地看到&*“取消”并将您留在(ptr + 5),这只涉及指针增量,而不涉及内存的负载。

C使这有点混浊,因为它区分左值和左值。也就是说,引用内存的表达式在表达式的左侧与右侧不同。给定像x = y;这样的语句,C编译器将从地址y加载一个值,并将其存储在地址x中。这是区别:y隐含解除引用,但是x不是。

5

您的期望是没有根据的。当您取消引用空指针时,C程序不一定会“崩溃”。 C程序展示所谓的未定义的行为当您试图做这样的事情。未定义的行为可以以许多不同的方式表现出来。它可能导致崩溃。或者它可以产生一些甚至类似于“工作”程序的东西。后者是你案件中显然发生的事情。

但无论如何,你的程序的行为是不确定的。不,它不会产生一个“有效的地址”,因为你似乎错误地相信。与内存中没有对象存在的位置相对应的数字地址无效(当然,空指针值除外)。