简体   繁体   English

使用指针访问结构元素

[英]Accessing structure elements using pointers

I got surprised when the following program did not crash. 当下面的程序没有崩溃时,我感到很惊讶。

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

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

As per my understanding the -> operator has higher precedence over & operator. 根据我的理解, ->运算符优先于&运算符。 So I expected the program to crash at the below statement when we try to dereference the NULL pointer tr . 所以当我们尝试取消引用NULL指针tr时,我希望程序在下面的语句中崩溃。

char *d = &ptr->b; 

But the statement &ptr->b evaluates to a valid address. 但语句&ptr->b计算为有效地址。 Could somebody please explain where I'm wrong? 有人可以解释一下我错了吗?

Your expectations were unfounded. 你的期望是没有根据的。 C programs don't necessarily "crash" when you dereference null pointers. 取消引用空指针时,C程序不一定“崩溃”。 C programs exhibit so called undefined behavior when you attempt to do something like that. 当您尝试执行类似的操作时,C程序会出现所谓的未定义行为 Undefined behavior can manifest itself in many different ways. 未定义的行为可以通过许多不同的方式表现出来。 It can result in a crash. 它可能导致崩溃。 Or it can produce something that even resembles a "working" program. 或者它可以产生甚至类似于“工作”程序的东西。 The latter is what apparently happened in your case. 后者显然是你的情况。

But in any case, your program's behavior is undefined. 但无论如何,你的程序的行为是不确定的。 And no, it does not produce a "valid address" as you seem to mistakingly believe. 不,它不会产生一个“有效地址”,因为你似乎错误地相信。 A numerical address that corresponds to a location in memory where no object exists is not valid (with the exception of null pointer value, of course). 对应于内存中没有对象存在的位置的数字地址无效(当然,空指针值除外)。

The reason that your code doesn't crash is that you didn't actually dereference the pointer. 您的代码没有崩溃的原因是您实际上没有取消引用指针。 Notice that the expression 注意表达式

&ptr->b

doesn't actually try loading the contents of ptr or ptr->b . 实际上并没有尝试加载ptrptr->b的内容。 Instead, it just stores the address of where this is located in memory. 相反,它只是存储它在内存中的位置。 What you'll end up getting is a pointer to where the b field of the object pointed at by ptr should be. 你最终得到的是指向ptr指向的对象的b字段的指针。 This will be a few bytes past address 0, so dereferencing the pointer you just created will cause a segfault. 这将是地址0之后的几个字节,因此取消引用刚创建的指针将导致段错误。

&ptr->b == sizeof(int) , that means the offset of b within _x after _x.a (which is of type int ) relative to the address *((x*)0) . &ptr->b == sizeof(int) ,表示_x.a (类型为int )之后_x内的b相对于地址*((x*)0)的偏移量。 The offset of 4 (typical for 32bit architecture) is saved within the d pointer. 偏移量为4 (典型值为32位架构)保存在d指针中。 You have to access to d in order to get an seg-fault. 您必须访问d才能获得seg-fault。

Computing an address does not require accessing memory. 计算地址不需要访问内存。 &ptr->b means "give me the address of the b field of the structure pointed to by ptr ." &ptr->b表示“给我ptr指向的结构的b字段的地址。” Doing so does not require looking at whatever may be stored in that memory location. 这样做不需要查看可能存储在该存储器位置的任何内容。

It may be helpful to think about indexing an array instead of a structure. 考虑索引数组而不是结构可能会有所帮助。 C defines ptr[5] as equivalent to *(ptr + 5) , which means that &(ptr[5]) is the same as &(*(ptr + 5)) . C将ptr[5]定义为等价于*(ptr + 5) ,这意味着&(ptr[5])&(*(ptr + 5)) Now it's easy to see that the & and the * "cancel out" and leave you with (ptr + 5) , which involves only a pointer increment, and not a load from memory. 现在很容易看到&* “取消”并留下(ptr + 5) ,它只涉及指针增量,而不是来自内存的负载。

C makes this slightly cloudy because it distinguishes lvalues from rvalues. C使它略微混浊,因为它区分左值和右值。 That is, an expression that refers to memory is treated differently on the left hand side of an expression than it is on the right. 也就是说,表达式的表达式在表达式的左侧与右侧的表达式区别对待。 Given a statement like x = y; 给出像x = y;的陈述x = y; , a C compiler will load a value from the address of y , and store it in the address of x . ,C编译器将从y的地址加载一个值,并将其存储在x的地址中。 This is the distinction: y is implicitly dereferenced, but x is not. 这是区别: y被隐式解除引用,但x不是。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM