2011-09-01 32 views
2

可能重複:
Program crashes when trying to set a character of a char array字符*海峽= 「...」 與字符STR [1] = 「...」 奇怪的行爲

我有一個示例代碼工作預期:

/* strtok example */ 
#include <stdio.h> 
#include <string.h> 

int main() 
{ 
    char str[] ="- This, a sample string."; 
    char * pch; 
    printf ("Splitting string \"%s\" into tokens:\n",str); 
    pch = strtok (str," ,.-"); 
/* 
    while (pch != NULL) 
    { 
    printf ("%s\n",pch); 
    pch = strtok (NULL, " ,.-"); 
    } 
*/ 
    return 0; 
} 

...除非我改變字符STR [1]爲char *海峽不應使任何語義差異:

/* strtok example */ 
#include <stdio.h> 
#include <string.h> 

int main() 
{ 
    char * str ="- This, a sample string."; 
    char * pch; 
    printf ("Splitting string \"%s\" into tokens:\n",str); 
    pch = strtok (str," ,.-"); 
/* 
    while (pch != NULL) 
    { 
    printf ("%s\n",pch); 
    pch = strtok (NULL, " ,.-"); 
    } 
*/ 
    return 0; 
} 

這是意想不到的結果:

Splitting string "- This, a sample string." into tokens: 
Segmentation fault 

我編譯兩個例子:

gcc -O0 main.c 
gcc -O3 main.c 
g++ -O0 main.c 
g++ -O3 main.c 

,甚至看着組裝......但我無法弄清楚,什麼是第二個版本錯誤。

這裏工作O1-大會:

.file "main.c" 
    .intel_syntax noprefix 
    .section .rodata.str1.8,"aMS",@progbits,1 
    .align 8 
.LC0: 
    .string "Splitting string \"%s\" into tokens:\n" 
    .section .rodata.str1.1,"aMS",@progbits,1 
.LC1: 
    .string " ,.-" 
    .text 
.globl main 
    .type main, @function 
main: 
.LFB58: 
    .cfi_startproc 
    push rbx 
    .cfi_def_cfa_offset 16 
    sub rsp, 48 
    .cfi_def_cfa_offset 64 
    mov rax, QWORD PTR fs:40 
    mov QWORD PTR [rsp+40], rax 
    xor eax, eax 
    mov DWORD PTR [rsp], 1750343725 
    mov DWORD PTR [rsp+4], 539784041 
    mov DWORD PTR [rsp+8], 1634934881 
    mov DWORD PTR [rsp+12], 1701605485 
    mov DWORD PTR [rsp+16], 1920234272 
    mov DWORD PTR [rsp+20], 778530409 
    mov BYTE PTR [rsp+24], 0 
    mov rdx, rsp 
    mov esi, OFFSET FLAT:.LC0 
    mov edi, 1 
    .cfi_offset 3, -16 
    call __printf_chk 
    mov esi, OFFSET FLAT:.LC1 
    mov rdi, rsp 
    call strtok 
    mov eax, 0 
    mov rdx, QWORD PTR [rsp+40] 
    xor rdx, QWORD PTR fs:40 
    je .L3 
    call __stack_chk_fail 
.L3: 
    add rsp, 48 
    pop rbx 
    .p2align 4,,1 
    ret 
    .cfi_endproc 
.LFE58: 
    .size main, .-main 
    .ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5" 
    .section .note.GNU-stack,"",@progbits 

和破一個:

.file "main.c" 
    .intel_syntax noprefix 
    .section .rodata.str1.1,"aMS",@progbits,1 
.LC0: 
    .string "- This, a sample string." 
    .section .rodata.str1.8,"aMS",@progbits,1 
    .align 8 
.LC1: 
    .string "Splitting string \"%s\" into tokens:\n" 
    .section .rodata.str1.1 
.LC2: 
    .string " ,.-" 
    .text 
.globl main 
    .type main, @function 
main: 
.LFB58: 
    .cfi_startproc 
    sub rsp, 8 
    .cfi_def_cfa_offset 16 
    mov edx, OFFSET FLAT:.LC0 
    mov esi, OFFSET FLAT:.LC1 
    mov edi, 1 
    mov eax, 0 
    call __printf_chk 
    mov esi, OFFSET FLAT:.LC2 
    mov edi, OFFSET FLAT:.LC0 
    call strtok 
    mov eax, 0 
    add rsp, 8 
    ret 
    .cfi_endproc 
.LFE58: 
    .size main, .-main 
    .ident "GCC: (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5" 
    .section .note.GNU-stack,"",@progbits 

唯一明顯的區別,我可以看到的是,在工作版本的GCC替換字符串用的MOV不變直接在代碼中。

幫助是非常讚賞

編輯 GCC(Ubuntu的/ Linaro的4.4.4-14ubuntu5)4.4.5,

一切順利, 托馬斯

+2

嗯...你是否搜索這個問題了?現在至少問了5次... – quasiverse

+0

請閱讀[c-faq](http://c-faq.com/)的第6部分。基本上它說**數組不是指針**和**指針不是數組**。因爲你已經在那裏,請閱讀其他章節:) – pmg

+0

請確定一種語言。 C和C++是不同的語言。 –

回答

5

在第二種情況下,您將str指向內存中某處無法更改的靜態對象。 strtok手冊頁警告說,它會更改其第一個參數,並且不能用於常量字符串。因此錯誤。

+0

嘿,停止投票給我。我們都是正確的,但Kerrek SB的答案要好得多。 –

5

strtok()需要一個可修改的緩衝區,因爲它用空字節替代了分隔符。所以你不能說char * str = "- This, a sample string.";,因爲那應該是const char * str = "- This, a sample string.";並指向只讀內存。取而代之的是,你有幾種選擇:

char str[] = "- This, a sample string."; // local array 
char * pch = strtok (str," ,.-"); 


char * str = strdup("- This, a sample string."); // malloc()ed 
char * pch = strtok (str," ,.-"); 
/* ... */ 
free(str); 
4

char * str分配供一個指針恰好是一個常量文字(即,不可寫)的字符串。

char str[]爲數組大小由分配的文字指定的數組分配空間。該數組是可寫的。

strtok()修改它工作的字符串。這是允許與str[]但不與*str

3

當你使用char[] p = "literal"時,許多編譯器將分配一個合適長度的字符數組,然後將字符串從字符串常量保存到的地方複製到數組中,所以最終得到可修改的字符串副本。

當你使用char* p = "literal"時,你有一個指向那個不可修改的字符串副本的指針。當您嘗試修改它時,行爲是未定義的。事實上,在某些時候,g ++在char *p = "literal"時開始發出警告,因爲指定它的正確方法是const char* p="literal",因爲它是一個指向常量字符串的指針。