2016-11-07 26 views
1

当运行这个程序:为什么不使用单位指针会导致分段错误?

#include<stdio.h> 

int main() 
{ 
    char *a[10]; 
    scanf("%s",a[0]); 
    printf("%s",a[0]); 
    return 0; 
} 

它似乎没有表现出分段故障正常工作。

由于数组a的每个元素都是一个尚未初始化的指针(即a[0]),为什么程序没有显示分段错误?

+16

因为“未定义的行为”意味着“任何事情都可能发生”,包括“程序完美无缺” –

+4

因为你很幸运 –

+0

因为你不是通过*写入*未初始化的指针,所以你正在*写入存储空间为指针。我95%肯定已经有了一个更详细的答案,但我无法从头顶找到答案。 – zwol

回答

0

当您取消引用未初始化的指针时,您调用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) 

无段错误这个时间,但是 “(空)” 被打印,而不是输入字符串 “hello”。

在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\[email protected]", 0x7fffffffe778 "", 0x3fa4403a90 "", 0x0, 
    0x2aaaaaaaf630 "\021\[email protected]", 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

总而言之,对未定义的行为没有任何保证。为了避免这种情况,一定要编译启用所有警告(对GCC为-Wall -Wextra),并使用Valgrind等内存检查器来捕捉您正在读取或写入不应该的位置。

相关问题