各位大佬,求帮分析一下这段 C 代码

2018-03-22 12:50:38 +08:00
 mrzys

C 小白一个,求各位大佬关照。问题代码见下:

#include <stdio.h>

int main() {
    long i;
    long j;
    char *ch; // 这里的确是需要初始化
    scanf("%s", ch);
    i = 0;  // 如果把这行注释掉,程序不会报错
    j = 0; //
}

执行结果:

root@ubuntu:~# ./a.out
v2ex
Segmentation fault

但是把下面给ij赋值的语句注释掉,或者只注释其中一条,却不会报错了:

root@ubuntu:~# ./a.out
v2ex

我是用objdump -d main.o查看反汇编的代码:

0000000000400546 <main>:
  400546:       48 83 ec 08             sub    $0x8,%rsp
  40054a:       be 00 00 00 00          mov    $0x0,%esi
  40054f:       bf f4 05 40 00          mov    $0x4005f4,%edi
  400554:       b8 00 00 00 00          mov    $0x0,%eax
  400559:       e8 d2 fe ff ff          callq  400430 <__isoc99_scanf@plt>
  40055e:       b8 00 00 00 00          mov    $0x0,%eax
  400563:       48 83 c4 08             add    $0x8,%rsp
  400567:       c3                      retq
  400568:       0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)

发现scanfch指针的地址赋值为0x0,这里的确是有问题,但是我想不通scanf下面的语句为什么会影响到程序的执行。

求各位大佬指教啊。。。

3439 次点击
所在节点    程序员
32 条回复
lance6716276
2018-03-22 12:55:19 +08:00
先把代码写标准了再去 debug
shuax
2018-03-22 12:57:03 +08:00
char *ch 只是一个指针,都没有给他固定空间,然后就越界了。
yinanc
2018-03-22 13:01:12 +08:00
赋值给未分配空间的指针时情况就不可控了,后面发生什么都有可能
shsdust
2018-03-22 13:06:59 +08:00
不是指明了 Segmentation fault 吗?表示 CPU 试图访问无法定址的块,明显是因为指针没被分配空间,既然到这里已经出现了这种错误,下面的程序报错很正常的
shsdust
2018-03-22 13:07:39 +08:00
还有,编译是一门玄学
mrzys
2018-03-22 13:16:19 +08:00
@shsdust 但是这样并不会报错:
```
#include <stdio.h>

int main() {
long i;
long j;
char *ch;
scanf("%s", ch);
i = 0;
//j = 0;
}
~
```
这就是令人奇怪的地方。` scanf`后面的代码会影响到程序会不会发生异常退出。
jiutai21
2018-03-22 13:19:27 +08:00
未定义的行为把,换个平台 /编译器结果不一定的
tomychen
2018-03-22 13:20:09 +08:00
1.ch 没空间
2.注释掉那行后没出错有没有可能是因为刚好 scanf 到 ch 的值刚好复盖到了第二个 long 上,所以没报错,你可以试动态调试一下。或者简单的把输入内容调整>sizeof(long)。
3. @shsdust 楼上说了玄学,这种东西代码在你的环境不出错不表示任何环境不出错,c 的指针野起来不是人能预见的。
liuzhedash
2018-03-22 14:14:05 +08:00
@mrzys #6
兄弟,这样写是否报错并不是你想的那种因果关系,建议阅读《深入理解计算机系统》的第三章。
doun
2018-03-22 14:17:14 +08:00
大概因为 scanf 下面的 int i,j 编译后,也是挪到栈上的吧
mrzys
2018-03-22 14:47:40 +08:00
@liuzhedash 就在阅读 csapp,但是对于内存这一块还是很模糊。第 7 章 linker 中说了局部变量是在执行的时候在 stack 上动态分配的,但是我反汇编之后,查看不到这段代码中的 i 和 j 的赋值指令。只有`sub $0x8,%rsp`,栈指针向下增加了 8 个字节,函数执行完毕返回之前执行`add $0x8,%rsp`,栈指针向上移动了 8 个字节,栈指针完全没有被影响。
lingdux
2018-03-22 14:52:36 +08:00
那里的野指针需要申请空间,大小要足够容纳输入的内容。
注释掉那句不发生异常只是堆栈压栈出栈的巧合而已,程序并没有按照你的预期执行。

这种问题动态调式,注意一下堆栈和内存,一目了然。
7 楼不懂别瞎说误导新人。
mrzys
2018-03-22 14:57:25 +08:00
@lingdux 谢谢大佬,晚上回去动态调试一下。
ysc3839
2018-03-22 15:01:40 +08:00
@lingdux 想问一下 7 楼说的怎么误导了?
lingdux
2018-03-22 15:05:32 +08:00
@ysc3839

未定义的行为很扯淡
lingdux
2018-03-22 15:09:52 +08:00
@mrzys
这个问题让我想起来《 0day 安全:软件漏洞分析技术》里面一个例子
虽然那个是 win 的,但是一样的原理。
2 . 2 修改邻接变量中,修改了相邻的变量,填补了被破坏的堆栈,导致程序改变了流程实现了破解 CrackMe。
pkookp8
2018-03-22 15:10:58 +08:00
尝试了 x86 和 arm,都不会出错
gcc5.3.1
armgcc4.4.1
编译方式
$(CC) src.c -o err.bin
不加其他编译参数
pkookp8
2018-03-22 15:15:22 +08:00
@pkookp8 尝试增加-g 和-O0 也没用。。。。
ysc3839
2018-03-22 15:16:52 +08:00
@lingdux 为什么这么说呢?
lingdux
2018-03-22 15:38:38 +08:00
@ysc3839
别问了,不想回复你了,感觉挺没意思的。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/440387

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX