【问题标题】:Why do I get segmentation fault here [duplicate]为什么我在这里遇到分段错误[重复]
【发布时间】:2019-11-02 06:20:09
【问题描述】:

我已将代码从 here 修改为以下代码中的 char[50] 为 char*:

#include <stdio.h>
#include <string.h>
int main ()
{
  // change made in following line from char string[50]
  char *string ="Test,string1,Test,string2:Test:string3"; 
  char *p;
  printf ("String  \"%s\" is split into tokens:\n",string);
  p = strtok (string,",:");
  while (p!= NULL)
  {
    printf ("%s\n",p);
    p = strtok (NULL, ",:");
  }
  return 0;
}

但是,我得到segmentation fault 上面的代码。

如何在上面的代码中使用指针版本?

另外,segmentation fault 是否会损坏磁盘上的数据?

【问题讨论】:

    标签: c string segmentation-fault strtok string-literals


    【解决方案1】:

    回答您的问题。 这就是您收到段错误的原因

    字符串字面量(例如使用char *string="Test,string1,Test,string2:Test:string3"; 声明的内容)和字符数组(使用char[50] 引用的版本)之间存在根本区别。

    为了提供对问题所在的不同视角,以下是编译期间发生的情况。

    在这两种情况下,常量字符串"Test,string1,Test,string2:Test:string3" 都存储在二进制文件的只读数据部分中。当您使用char *string 时,您正在将常量字符串(在.rodata 中)的位置(指针)分配给堆栈上的变量。当您使用char string[50] 时,您实际上是在声明一个字符数组作为堆栈上的存储,而不是一个字符指针。编译器实际上以与您预期不同的方式执行此分配。在很多情况下,它会添加memcpy等函数调用来初始化字符数组。像这样的:

    char string [50]
    memcpy(string,"Test,string1,Test,string2:Test:string3",0x32);
    

    这具有创建本地堆栈变量的优势,该变量可以通过strtok 等其他函数进行操作。但是,您当然不能使用相同的函数来操作二进制文件只读部分中的原始字符串。这就是根本的区别。

    来自莫斯科的@Vlad 提到的所有其他内容也是相关的。

    下一个问题:还有,segmentation fault 是否会损坏磁盘上的数据?

    当在不允许此类操作的内存段中发生操作(读取、写入、执行)时,会发生分段错误。大多数情况下,这发生在尝试读取或写入无效指针引用的位置时。这完全是一个运行时概念。故障包含在进程的虚拟内存中。一般来说,这不会对任何二级存储造成伤害。可能存在辅助存储中的文件可能已损坏的极端情况(部分将数据写入文件后发生段错误),但您显示的示例并非如此。总而言之,除非在写入磁盘的过程中发生段错误,否则您的磁盘应该没问题。

    【讨论】:

    • 感谢您提供详细解释的答案。多年前确实发生过一次,磁盘数据严重损坏(该程序也有磁盘写入)。这些年我没碰过C!
    【解决方案2】:

    在此声明中

    char *string ="Test,string1,Test,string2:Test:string3"; 
    

    定义了一个指针,指向字符串文字的第一个字符。

    然后您尝试使用指针来更改字符串文字。

    考虑到标准函数strtok 更改了传递给它的字符串,在分隔符处插入了空终止字符。

    您不能更改 C(和 C++)中的字符串文字。它们是不可变的。任何更改字符串文字的尝试都会导致未定义的行为。

    您可以使用函数strspnstrcspn 来代替函数strtok 来提取令牌。在这种情况下,您可以处理字符串文字,因为这些函数不会更改传递给它们的字符串。

    【讨论】: