C语言结构体指针成员访问:两段示例代码运行差异原因求解
问题原因解析
核心本质:野指针访问导致的未定义行为
首先明确前提:C语言中函数内部定义的非静态局部变量,不会被自动初始化,其值为对应内存地址中遗留的随机垃圾值。
你两段代码的核心操作SECOND->second->a = 9,本质上是三层访问:
- 第一步:
SECOND指向你定义的栈变量b,这一步是合法的(你已经执行了SECOND = &b) - 第二步:访问
b的成员second,这是一个struct first*类型的指针,但是你从来没有显式给这个指针赋值过,它的值是随机垃圾值 - 第三步:通过这个垃圾值指针访问它的成员
a,属于典型的野指针访问,是C语言标准定义的未定义行为,程序可能崩溃、可能输出随机值,也可能碰巧正常运行。
为什么加了被注释的两行代码就碰巧能运行
加了那两行代码后,栈内存的分配顺序是:
- 分配
struct first C变量,这是一块合法的struct first类型内存空间 - 分配指针变量
FIRST,并把它的值设为&C,也就是C变量的地址 - 接下来分配
struct second b变量,它的第一个成员second对应的内存地址,刚好残留了之前写入的&C的值
相当于碰巧让b.second指向了合法的C变量,这时候访问b.second->a就不会触发非法内存访问,所以程序碰巧能正常运行。
注意:这只是巧合,完全依赖编译器的栈分配规则和运行环境,换个编译器或者编译选项可能就会崩溃,本质仍然是错误代码。大概率是你写第一段代码时漏了显式给
SECOND->second赋值的步骤,才会出现这种靠运气运行的情况。
为什么注释掉那两行就会崩溃
注释掉那两行后,栈上不会分配C变量和FIRST指针,b.second的垃圾值就是完全随机的非法地址,直接通过这个地址写入9会触发操作系统的内存保护,直接报段错误崩溃。
正确的写法
要避免野指针,必须显式给b.second赋值,不要依赖栈内存的巧合值,修改后的代码如下:
#include<stdio.h> int main() { struct first{ char *name; int a; }; struct second{ struct first *second; int z; }; struct first C; // 分配实体变量 struct second b; struct second *SECOND = &b; // 显式给指针成员赋值,不要依赖默认值 SECOND->second = &C; SECOND->second->a = 9; printf("%d", SECOND->second->a); return 0; }
内容的提问来源于stack exchange,提问作者Tashif Iqbal
相关产品推荐
相关产品推荐

