修改后续内存位置值时指针变量值异常变化的原因排查
a意外修改及段错误的原因分析 问题复现
第一段代码
int *a, *b,*c,d; a=&d; printf("1 a=%p &d=%p\n", a,&d); b=a+1; printf("2 a=%p &d=%p b=%p\n", a,&d,b); c=a+2; printf("3 a=%p &d=%p b=%p c=%p\n", a, &d,b,c); *b=34; printf("4 a=%p &d=%p b=%p c=%p\n", a, &d,b,c); d=342; printf("5 a=%p &d=%p b=%p c=%p\n", a, &d,b,c); *c=21; printf("6 a=%p &d=%p b=%p c=%p\n", a, &d,b,c); *b=37; printf("7 a=%p &d=%p b=%p c=%p\n", a, &d,b,c); *c=29; printf("8 a=%p &d=%p b=%p c=%p\n", a, &d,b,c);
实际输出:
1 a=0x7ffd0267145c &d=0x7ffd0267145c
2 a=0x7ffd0267145c &d=0x7ffd0267145c b=0x7ffd02671460
3 a=0x7ffd0267145c &d=0x7ffd0267145c b=0x7ffd02671460 c=0x7ffd02671464
4 a=0x7ffd00000022 &d=0x7ffd0267145c b=0x7ffd02671460 c=0x7ffd02671464
5 a=0x7ffd00000022 &d=0x7ffd0267145c b=0x7ffd02671460 c=0x7ffd02671464
6 a=0x1500000022 &d=0x7ffd0267145c b=0x7ffd02671460 c=0x7ffd02671464
7 a=0x1500000025 &d=0x7ffd0267145c b=0x7ffd02671460 c=0x7ffd02671464
8 a=0x1d00000025 &d=0x7ffd0267145c b=0x7ffd02671460 c=0x7ffd02671464
第二段修改后的代码
int *a, *b,*c,d; a=&d; printf("1 a=%p &d=%p\n", a,&d); b=a+4; printf("2 a=%p &d=%p b=%p\n", a,&d,b); c=a+8; printf("3 a=%p &d=%p b=%p c=%p\n", a, &d,b,c); *b=34; printf("4 a=%p &d=%p b=%p c=%p\n", a, &d,b,c); d=342; printf("5 a=%p &d=%p b=%p c=%p\n", a, &d,b,c); *c=21; printf("6 a=%p &d=%p b=%p c=%p\n", a, &d,b,c); *b=37; printf("7 a=%p &d=%p b=%p c=%p\n", a, &d,b,c); *c=29; printf("8 a=%p &d=%p b=%p c=%p\n", a, &d,b,c); return 0;
实际输出:
1 a=0x7ffc674b76dc &d=0x7ffc674b76dc
2 a=0x7ffc674b76dc &d=0x7ffc674b76dc b=0x7ffc674b76ec
3 a=0x7ffc674b76dc &d=0x7ffc674b76dc b=0x7ffc674b76ec c=0x7ffc674b76fc
4 a=0x7ffc674b76dc &d=0x7ffc674b76dc b=0x22674b76ec c=0x7ffc674b76fc
5 a=0x7ffc674b76dc &d=0x7ffc674b76dc b=0x22674b76ec c=0x7ffc674b76fc
6 a=0x7ffc674b76dc &d=0x7ffc674b76dc b=0x22674b76ec c=0x7ffc674b76fc
Segmentation fault (core dumped)
原因分析
两个问题的本质都是栈内存越界访问引发的未定义行为,具体细节如下:
1. 第一段代码中a被意外修改的逻辑
C语言中,局部变量(a、b、c、d)都存储在栈上,栈通常从高地址向低地址生长。从输出反推,你的栈布局中,d的内存地址之后(高地址方向)恰好是指针变量a的存储位置:
a是int*类型,值为&d(0x7ffd0267145c);执行b=a+1时,因为a是int*,指针加法会自动偏移sizeof(int)(4字节),所以b指向0x7ffd02671460——这个地址正好是a变量自身的存储地址。- 当执行
*b=34时,相当于直接修改a变量的内存内容:把a原本存储的&d(0x7ffd0267145c)覆盖为34(十六进制0x22),结合栈地址的高位数据,最终a的值变成0x7ffd00000022。 - 后续
c=a+2指向&d + 2*sizeof(int),也就是a变量存储地址的下一个4字节区域,修改*c会继续篡改a的高位字节,导致a的值持续异常。
2. 第二段代码触发段错误的逻辑
修改后b=a+4指向&d + 16字节,c=a+8指向&d +32字节:
- 初始
*b=34修改的是栈上的某个有效区域,仅破坏了局部变量b自身的值(输出中b变为0x22674b76ec),未触发直接错误。 - 执行
*c=21时,c指向的地址已经超出当前栈帧的有效范围,访问了不属于当前进程的内存,或者触发了栈保护机制(如金丝雀值检查),直接引发段错误。
核心本质
C语言不做指针访问的边界检查,当指针访问超出目标变量所属内存范围的地址时,属于未定义行为——可能修改其他变量、破坏栈帧、触发段错误,甚至出现无规律的诡异行为。你的代码中,b、c均指向d变量之外的栈内存,恰好覆盖了其他局部变量或栈关键数据,从而引发异常。
内容的提问来源于stack exchange,提问作者Gurjot Singh

