2016-11-07 25 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等內存檢查器來捕捉您正在讀取或寫入不應該的位置。

相關問題