【问题标题】:char* str="..." vs char str[]="..." strange behaviour [duplicate]char* str="..." vs char str[]="..." 奇怪的行为 [重复]
【发布时间】:2011-09-01 10:38:03
【问题描述】:

可能重复:
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-Assembly:

    .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,

一切顺利, 托马斯

【问题讨论】:

  • 嗯...你搜索过这个问题吗?它现在至少被问了 5 次......
  • 阅读the c-faq 的第 6 部分。基本上它说数组不是指针指针不是数组。既然你已经在那里,请阅读其他部分:)
  • 请选择一种语言。 C 和 C++ 是不同的语言。
  • “这不应该在语义上产生任何差异” - 你为什么会这样说? char str[] = "foo"; 定义了一个名为 str 的数组,并通过从字符串字面量复制来初始化其内容。 char *str = "foo"; 定义了一个名为 str 的指针,并将其初始化为指向字符串文字。完全不同的东西。此外,在 gcc 中编写新的 C 代码时,您通常可以使用 -Wwrite-strings 而不会收到任何虚假警告。这警告定义一个指向字符串文字的char*(它必须是const char*),在编译时发现问题。
  • 我将此标记为重复但现在我希望他们不要关闭它,因为有几个非常好的答案。

标签: c gcc g++ libc


【解决方案1】:

在第二种情况下,您将str 指向内存中某个无法更改的静态对象。 strtok 手册页警告它更改了它的第一个参数并且不能用于常量字符串。因此出现错误。

【讨论】:

  • 嘿,别再给我投票了。我们都是正确的,但 Kerrek SB 的答案要好得多。
【解决方案2】:

strtok() 需要一个 modifiable 缓冲区,因为它将分隔符替换为空字节。所以你不能说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);

【讨论】:

    【解决方案3】:

    char * str 为指向恰好是常量文字(即不可写)的字符串的指针分配空间。

    char str[] 为数组分配空间,数组的大小由分配的文字指定。数组是可写的。

    strtok() 修改它所处理的字符串。 str[] 允许这样做,但 *str 不允许这样做。

    【讨论】:

      【解决方案4】:

      当您使用char[] p = "literal" 时,许多编译器将分配一个适当长度的字符数组,然后将字符串从保存字符串常量的任何位置复制到数组中,因此您最终得到了字符串的可修改副本。

      当您使用char* p = "literal" 时,您有一个指向该字符串的不可修改副本的指针。当您尝试修改它时,行为是未定义的。事实上,当你执行char *p = "literal" 时,g++ 在某些时候开始发出警告,因为指定它的正确方法是const char* p="literal",因为它是一个指向常量字符串的指针。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-08-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-01
        • 2011-04-21
        • 2015-10-06
        • 2022-06-15
        相关资源
        最近更新 更多