为什么我会在这里出现分段错误
Why do I get segmentation fault here
我修改了代码 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
会不会损坏磁盘上的数据?
在此声明中
char *string ="Test,string1,Test,string2:Test:string3";
定义了一个指向字符串文字第一个字符的指针。
然后您尝试使用指针更改字符串文字。
考虑到标准函数 strtok
更改传递给它的字符串,在分隔符处插入空终止字符。
您不能更改 C(和 C++)中的字符串文字。它们是不可变的。任何更改字符串文字的尝试都会导致未定义的行为。
您可以使用函数 strspn
和 strcspn
来提取标记,而不是函数 strtok
。在这种情况下,您可以处理字符串文字,因为这些函数不会更改传递给它们的字符串。
回答您的问题。 这就是您收到段错误的原因:
字符串文字(例如用 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 提到的所有其他内容也是相关的。
下一个问题:另外,分段错误会不会损坏磁盘上的数据?
当在不允许此类操作的内存段中发生操作(读取、写入、执行)时,会发生段错误。大多数情况下,这发生在尝试读取或写入无效指针引用的位置时。这完全是一个运行时概念。故障包含在进程的虚拟内存中。一般来说,这不会对任何二级存储造成伤害。可能存在辅助存储中的文件可能已损坏的边缘情况(将数据部分写入文件后发生段错误),但您显示的示例不是这种情况。总而言之,除非在写入磁盘的过程中发生段错误,否则您的磁盘应该没问题。
我修改了代码 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
会不会损坏磁盘上的数据?
在此声明中
char *string ="Test,string1,Test,string2:Test:string3";
定义了一个指向字符串文字第一个字符的指针。
然后您尝试使用指针更改字符串文字。
考虑到标准函数 strtok
更改传递给它的字符串,在分隔符处插入空终止字符。
您不能更改 C(和 C++)中的字符串文字。它们是不可变的。任何更改字符串文字的尝试都会导致未定义的行为。
您可以使用函数 strspn
和 strcspn
来提取标记,而不是函数 strtok
。在这种情况下,您可以处理字符串文字,因为这些函数不会更改传递给它们的字符串。
回答您的问题。 这就是您收到段错误的原因:
字符串文字(例如用 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 提到的所有其他内容也是相关的。
下一个问题:另外,分段错误会不会损坏磁盘上的数据?
当在不允许此类操作的内存段中发生操作(读取、写入、执行)时,会发生段错误。大多数情况下,这发生在尝试读取或写入无效指针引用的位置时。这完全是一个运行时概念。故障包含在进程的虚拟内存中。一般来说,这不会对任何二级存储造成伤害。可能存在辅助存储中的文件可能已损坏的边缘情况(将数据部分写入文件后发生段错误),但您显示的示例不是这种情况。总而言之,除非在写入磁盘的过程中发生段错误,否则您的磁盘应该没问题。