简体   繁体   中英

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 .

char *d = &ptr->b; 

But the statement &ptr->b evaluates to a valid address. Could somebody please explain where I'm wrong?

Your expectations were unfounded. C programs don't necessarily "crash" when you dereference null pointers. C programs exhibit so called undefined behavior when you attempt to do something like that. 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 . 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. This will be a few bytes past address 0, so dereferencing the pointer you just created will cause a segfault.

&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) . The offset of 4 (typical for 32bit architecture) is saved within the d pointer. You have to access to d in order to get an 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 ." 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)) . 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.

C makes this slightly cloudy because it distinguishes lvalues from rvalues. 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; , a C compiler will load a value from the address of y , and store it in the address of x . This is the distinction: y is implicitly dereferenced, but x is not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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