Answers
虽然不是
焦点访谈
,但也可以用实事说话..
有图有真相:
但是, 当我把编译好的程序放在 调试器 里调试的时候, 情况就又不一样了.
反汇编后的汇编代码:
Address Hex dump Command Comments Label
01091000 /$ 55 PUSH EBP
01091001 |. 8BEC MOV EBP,ESP
01091003 |. 51 PUSH ECX
01091004 |. 6A 64 PUSH 64 /Arg1 = 64 (这里的 64是16进制, 10进制对应的是100)
01091006 |. E8 5D020000 CALL 01091268 \malloc
0109100B |. 83C4 04 ADD ESP,4
0109100E |. 8945 FC MOV DWORD PTR SS:[LOCAL.1],EAX /将返回值保存
01091011 |. 68 00B00901 PUSH OFFSET 0109B000 ASCII "hello"
01091016 |. 8B45 FC MOV EAX,DWORD PTR SS:[LOCAL.1]
01091019 |. 50 PUSH EAX
0109101A |. E8 51010000 CALL 01091170 /strcpy(str, "hello")
0109101F |. 83C4 08 ADD ESP,8
01091022 |. 8B4D FC MOV ECX,DWORD PTR SS:[LOCAL.1]
01091025 |. 51 PUSH ECX /Arg1 => [LOCAL.1]
01091026 |. E8 FE000000 CALL 01091129 \free
0109102B |. 83C4 04 ADD ESP,4
0109102E |. 837D FC 00 CMP DWORD PTR SS:[LOCAL.1],0 /拿 LOCAL.1 和 0 进行比较
01091032 |. 74 1D JE SHORT 01091051 \如果相等, 则跳(即 if(str == NULL)
01091034 |. 68 08B00901 PUSH OFFSET 0109B008 ASCII "world"
01091039 |. 8B55 FC MOV EDX,DWORD PTR SS:[LOCAL.1]
0109103C |. 52 PUSH EDX
0109103D |. E8 2E010000 CALL 01091170 /strcpy(str, "world")
01091042 |. 83C4 08 ADD ESP,8
01091045 |. 8B45 FC MOV EAX,DWORD PTR SS:[LOCAL.1]
01091048 |. 50 PUSH EAX
01091049 |. E8 1E000000 CALL 0109106C /printf(str)
0109104E |. 83C4 04 ADD ESP,4
01091051 |> 8BE5 MOV ESP,EBP
01091053 |. 5D POP EBP
01091054 \. C3 RETN
call 之前
call 之后
可以看到它并未变成
NULL
, 所以后面的
JE
并不会跳, 从而会将
world
复制过去, 然后输出的结果为
world
.
在楼主所提供的四个答案中,
hello
和
helloworld
可以被排除, 那么只剩下
world
和
未定义行为
这两个 选项了, 而上面的例子又可以证明它有时候会输出
world
, 而有时候又不会输出
world
, 所以选择
world
也是不太恰当的, 那么就只剩下
未定义行为
这个答案了.
附在
linux
下运行的结果:
战场原灰原哀
answered 9 years, 1 month ago
上面用汇编确实好厉害呀,但是不一定都看得懂。从C语言层分析的话,应该是这样的。
char * str = malloc(100);
malloc分配的内存指针赋给str,这段内存并未初始化,所以是非NULL的。
strcpy(str, "hello");
将"hello"拷贝到str指向的内存
free(str);
释放str指向的内存,但内存被释放后,很有可能该指针仍然指向该内存单元,此时这段内存已经不再属于这个程序,str也就是一个非NULL的 悬空指针 了。
后续的
strcpy(str, "world");
也就是非法操作了!所以是未定义行为。
O4LOVE
answered 9 years, 1 month ago