运行此程序时:
#include<stdio.h>
int main()
{
char *a[10];
scanf("%s",a[0]);
printf("%s",a[0]);
return 0;
}
如果没有显示分段错误,它似乎完美无缺。
由于数组a
的每个元素都是一个尚未初始化的指针(即a[0]
),为什么程序没有显示分段错误?
答案 0 :(得分:1)
取消引用未初始化的指针时,可以调用undefined behavior。
虽然这经常导致崩溃,但并非必然。这就是为什么它被称为未定义的行为。程序可能会崩溃,它可能以意想不到的方式运行,或者(如您所见)可能看起来正常工作。这种行为可能会随着看似无关的代码更改而发生变化,例如添加一个或多个局部变量。
这也意味着您无法依赖任何特定行为。如果您使用其他编译器,或在不同的计算机上构建,则可以获得不同的结果。
让我们更多地说明未定义的行为。当我运行你的代码时,我遇到了分段错误,而对你来说它似乎正常运行。
以下是我在Valgrind下运行时获得的输出:
==1047== Memcheck, a memory error detector
==1047== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==1047== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==1047== Command: /tmp/x1
==1047==
hello
==1047== Conditional jump or move depends on uninitialised value(s)
==1047== at 0x3FA445345F: _IO_vfscanf (in /lib64/libc-2.5.so)
==1047== by 0x3FA445DCAB: scanf (in /lib64/libc-2.5.so)
==1047== by 0x4004F2: main (x1.c:6)
==1047==
==1047== Use of uninitialised value of size 8
==1047== at 0x3FA44534D3: _IO_vfscanf (in /lib64/libc-2.5.so)
==1047== by 0x3FA445DCAB: scanf (in /lib64/libc-2.5.so)
==1047== by 0x4004F2: main (x1.c:6)
==1047==
==1047==
==1047== Process terminating with default action of signal 11 (SIGSEGV)
==1047== Bad permissions for mapped region at address 0x400520
==1047== at 0x3FA44534D3: _IO_vfscanf (in /lib64/libc-2.5.so)
==1047== by 0x3FA445DCAB: scanf (in /lib64/libc-2.5.so)
==1047== by 0x4004F2: main (x1.c:6)
==1047==
==1047== HEAP SUMMARY:
==1047== in use at exit: 0 bytes in 0 blocks
==1047== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==1047==
==1047== All heap blocks were freed -- no leaks are possible
==1047==
==1047== For counts of detected and suppressed errors, rerun with: -v
==1047== Use --track-origins=yes to see where uninitialised values come from
==1047== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 4 from 4)
您可以从此输出中看到正在使用未初始化的变量,这随后会导致分段违规。
在gdb下运行,我明白了:
GNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-45.el5)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /tmp/x1...done.
(gdb) start
Temporary breakpoint 1 at 0x4004e0: file /tmp/x1.c, line 8.
Starting program: /tmp/x1
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x2aaaaaaab000
Temporary breakpoint 1, main () at /tmp/x1.c:8
8 scanf("%s",a[0]);
(gdb) p a
$1 = {0x400520 "L\211d$\340L\211l$\350L\215%\223\001 ",
0x4003bb "H\203\304\b\303\377\065\312\004 ",
0xca000000000001 <Address 0xca000000000001 out of bounds>,
0x400557 "H\215\005f\001 ", 0x0, 0x3fa421cbc0 "",
0x400520 "L\211d$\340L\211l$\350L\215%\223\001 ", 0x0,
0x7fffffffe860 "\001", 0x0}
(gdb) step
hello
Program received signal SIGSEGV, Segmentation fault.
0x0000003fa44534d3 in _IO_vfscanf_internal () from /lib64/libc.so.6
(gdb)
记下a
包含的内容。现在,我做了一个小改动:
#include<stdio.h>
int main()
{
int x[100];
char *a[10];
int y[100];
scanf("%s",a[0]);
printf("%s",a[0]);
return 0;
}
我在a
之前和之后添加了一个局部变量。在一个表现良好的计划中,这不会改变任何事情。但是当存在未定义的行为时,所有的赌注都会被取消。
在Valgrind下运行:
==1392== Memcheck, a memory error detector
==1392== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==1392== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==1392== Command: /tmp/x1
==1392==
hello
==1392== Conditional jump or move depends on uninitialised value(s)
==1392== at 0x3FA445345F: _IO_vfscanf (in /lib64/libc-2.5.so)
==1392== by 0x3FA445DCAB: scanf (in /lib64/libc-2.5.so)
==1392== by 0x4004F8: main (x1.c:8)
==1392==
==1392== Conditional jump or move depends on uninitialised value(s)
==1392== at 0x3FA4443D1C: vfprintf (in /lib64/libc-2.5.so)
==1392== by 0x3FA444CD09: printf (in /lib64/libc-2.5.so)
==1392== by 0x40050E: main (x1.c:9)
==1392==
(null)==1392==
==1392== HEAP SUMMARY:
==1392== in use at exit: 0 bytes in 0 blocks
==1392== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==1392==
==1392== All heap blocks were freed -- no leaks are possible
==1392==
==1392== For counts of detected and suppressed errors, rerun with: -v
==1392== Use --track-origins=yes to see where uninitialised values come from
==1392== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 4 from 4)
这次没有段错,但是#34;(null)&#34;打印而不是输入字符串&#34;你好&#34;。
在gdb下:
GNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-45.el5)
Copyright (C) 2009 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /tmp/x1...done.
(gdb) start
Temporary breakpoint 1 at 0x4004e3: file /tmp/x1.c, line 8.
Starting program: /tmp/x1
warning: no loadable sections found in added symbol-file system-supplied DSO at 0x2aaaaaaab000
Temporary breakpoint 1, main () at /tmp/x1.c:8
8 scanf("%s",a[0]);
(gdb) p a
$1 = {0x0, 0x7fffffffe5d0 "", 0xf63d4e2e <Address 0xf63d4e2e out of bounds>,
0x7fffffffe760 "0\005@", 0x7fffffffe778 "", 0x3fa4403a90 "", 0x0,
0x2aaaaaaaf630 "\021\003@", 0x2aaaaaaaf0f0 "", 0x4002ff "__libc_start_main"}
(gdb) step
hello
9 printf("%s",a[0]);
(gdb)
10 return 0;
(gdb)
11 }
(gdb)
0x0000003fa441d9f4 in __libc_start_main () from /lib64/libc.so.6
(gdb)
Single stepping until exit from function __libc_start_main,
which has no line number information.
(null)
Program exited normally.
(gdb) quit
特别要注意每种情况下a
的内容。对于原始计划,a[0]
包含0x400520
,而修改后的计划a[0]
包含0x0
。
总而言之,对于未定义的行为,无法保证。为了避免这种情况,请确保编译将启用所有警告(-Wall -Wextra
用于GCC)并使用像Valgrind这样的记忆检查器来捕捉您不应该阅读或写入您不应该的地方的情况。< / p>