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

为何这段C代码未触发Segfault?求技术解释

为什么这段C语言代码没有触发预期的段错误?

先看你的代码:

#include <stdlib.h>
#include <stdio.h>

typedef struct tag_node {
    struct tag_node *left, *right, *prev;
    int value;
} node;

int main()
{
    node *root_node = malloc(sizeof(node));
    root_node->value = 1;
    node **position = &root_node->right;
    *position = malloc(sizeof(node));
    (*position)->value = 2;
    (*position)->prev = root_node;
    printf("root node is %d, right node is %d\n",
        root_node->value, root_node->right->value);
    node **ptr = &(*position)->prev->prev->prev;
    printf("This is fine\n");
    return 0;
}

编译命令:gcc -Wall -Wextra -g -O0 example.c -o example

你预期在node **ptr = &(*position)->prev->prev->prev;行触发段错误,但实际程序正常运行,原因主要有两点:


1. 你对root_node->prev的初始值有误解

malloc函数只负责分配内存,不会初始化内存内容。root_node是malloc出来的,它的prev成员是未初始化的垃圾值,不是你以为的NULL。不过即使root_node->prev确实是NULL,接下来的逻辑依然能解释程序不崩溃的原因。

2. 关键表达式并没有实际解引用无效内存

拆解&(*position)->prev->prev->prev的运算逻辑:
根据C语言运算符优先级,->优先级高于&且右结合,这个表达式等价于:

&( ( ( (*position)->prev )->prev )->prev )

假设( (*position)->prev )->prev是NULL,那么表达式简化为&(NULL->prev)。这里的核心是:取空指针指向的结构体成员的地址,不等于访问该成员的内容。

在大多数编译器实现中,结构体成员的偏移量是编译期就确定的。比如node结构体里prev成员的偏移量,可以用offsetof(node, prev)计算。当编译器处理&ptr->member时,会把它转换成(char*)ptr + offsetof(node, member)的地址计算。如果ptr是NULL,这个计算结果就是offsetof(node, prev)(0加上偏移量),这是一个无效地址,但你的代码只是把这个地址赋值给ptr变量,既没有读取这个地址的内容,也没有向它写入数据。

段错误是操作系统在程序试图读写无效内存区域时才会触发的,仅仅计算并存储一个无效地址并不会触发错误。


最后要注意:这种行为属于C标准定义的未定义行为,不同编译器、平台可能有不同表现,绝对不能依赖这种代码逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 10:02:42