2011-09-01 52 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",因为它是一个指向常量字符串的指针。