可能重复:
Program crashes when trying to set a character of a char array
我有一个按预期工作的示例代码:
/* 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;
}
...除非我将char str []更改为char * str,这不应该在语义上产生任何差异:
/* 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,
一切顺利, 托马斯
答案 0 :(得分:5)
在第二种情况下,您将str
指向内存中某个无法更改的静态对象。 strtok
手册页警告它更改了第一个参数,不能用于常量字符串。因此错误。
答案 1 :(得分: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);
答案 2 :(得分:4)
char * str
为指向字符串的指针分配空间,该字符串恰好是一个常量字面值(即不可写)。
char str[]
为数组分配空间,该数组的大小由指定的文字指定。该数组是可写的。
strtok()
修改它所使用的字符串。 str[]
允许这样做,但*str
不允许这样做。
答案 3 :(得分:3)
使用char[] p = "literal"
时,许多编译器将分配一个适当长度的字符数组,然后将字符串从保存字符串常量的地方复制到数组中,因此最终会得到可修改的副本字符串。
当您使用char* p = "literal"
时,您有一个指向该字符串的不可修改副本的指针。当您尝试修改它时,行为是未定义的。事实上,在某些时候,当你执行char *p = "literal"
时,g ++开始发出警告,因为指定它的正确方法是const char* p="literal"
,因为它是指向常量字符串的指针。