This is a example code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct substruct {
int integer1;
unsigned char boolean1;
unsigned char boolean2;
char *ptr1;
char *ptr2;
} substruct_t;
typedef struct tester {
char *ptr1; //0x00
char *ptr2; //0x08
unsigned char boolean1; //0x10
unsigned char boolean2; //0x11
int integer1; //0x12
// padding bypte 0x16 -> 0x18
char *ptr3; //0x18
// It is not pointer.
substruct_t local; // 0x20
char *ptr4;
} tester_t;
int main()
{
tester_t *tester;
tester = NULL;
printf("0\n");
substruct_t *localptr = &(tester->local);
printf("1\n");
// seg fault
substruct_t test_local = tester->local;
printf("2\n");
substruct_t *test_local_ptr = &test_local;
printf("3\n");
// localptr = &(0x20); ???
// void *voidptr = 0x20;
// substruct_t *testptr = (substruct_t *)&(voidptr);
// printf("4\n");
return 0;
}
Why doesn’t the program die at &(tester->local)
line?
I want to know why test->local
causes a crash, but &(test->local)
doesn’t.
This is the output result.
$ gcc -Wall -g -o asdf asdf.c
$ ./asdf
0
1
[1] 1777 segmentation fault ./asdf
0, 1 print but 2 not print. seg fault at tester->local
line.
Both lines dereference a NULL pointer, and doing so triggers undefined behavior in your code.
With undefined behavior, there is no guarantee what your code will do. It might crash, it might print strange results, or it might (as in your case with the first line) appear to work properly. Additionally, making a seemingly unrelated code change, such as adding an unused local variable or a call to printf
for debugging, can change how undefined behavior manifests.
Just because your code could crash doesn’t mean it will.
That being said, what’s probably happening is that the compiler looks at &(tester->local)
and simply calculates the offset of the local
member and adds that to the current value of tester
without actually performing a dereference. But again, this is undefined behavior and there’s no guarantee of this.
As the offending lines of code don’t do anything, it’s not inconceivable that a compiler could optimize them away, eliminating the undefined behavior.