You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何对NULL指针的结构体成员取地址不会导致程序崩溃?

NULL指针访问结构体成员的差异解析

示例代码

#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 byte 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;
}

编译运行结果

$ gcc -Wall -g -o asdf asdf.c
$ ./asdf
0
1
[1]    1777 segmentation fault  ./asdf

问题

程序执行&(tester->local)(tester为NULL指针)时未崩溃,成功打印出1,但执行substruct_t test_local = tester->local;时触发段错误,无法打印2。为何会出现这种差异?


原因解析

  • 取地址操作仅做偏移计算:&(tester->local)的本质是编译器根据tester_t的内存布局,计算local成员相对于结构体起始地址的偏移量(这里是0x20)。因为tester是NULL(起始地址为0),所以最终得到的地址是0 + 0x20 = 0x20。整个过程只是数学上的地址计算,没有实际去读取或写入该地址对应的内存,因此不会触发内存访问违规。
  • 直接访问需要读取内存:substruct_t test_local = tester->local;是要把tester->local这个结构体成员的所有内容复制到test_local中。这就需要程序去访问地址0x20处的内存,读取其中的数据。而NULL指针指向的低地址区域属于操作系统保护的内存空间,用户程序无权访问,一旦尝试读取就会触发段错误。
  • 标准与实现的说明:C标准中,对NULL指针直接解引用(比如访问成员)属于未定义行为;但取成员地址的操作,在GCC这类常见编译器中只是做偏移计算,没有实际解引用内存,因此不会崩溃。不过这种行为是编译器的具体实现,标准并没有保证它一定安全,不要依赖这种特性写代码。

内容的提问来源于stack exchange,提问作者kiyo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 17:02:03