为什么改变一个非const char*会导致分割错误

Why a segmentation fault for changing a non-const char*?

本文关键字:char 错误 分割 const 改变 一个 为什么      更新时间:2023-10-16

使用此代码,我得到一个分段错误:

   char* inputStr = "abcde";
   *(inputStr+1)='f';

如果代码是:

   const char* inputStr = "abcde";
   *(inputStr+1)='f';

我将得到"分配只读位置"的编译错误。然而,对于第一种情况,没有编译错误;只是赋值操作实际发生时的分割错误。

有人能解释一下吗?

以下是标准在第[2.13.4/2]节中对字符串字面量的说明:

不以u、u或L开头的字符串字面值是普通字符串字面值,也称为窄字符串字面值。一个普通的字符串字面值的类型是" array of n const char ",其中n是字符串的长度,定义如下;它具有静态存储时间(3.7),并使用给定的字符进行初始化。

所以,严格来说,"abcde"的类型是

const char[6]

现在在代码中发生的是对

的隐式强制转换
char*

允许赋值。其原因很可能是与c兼容。请查看此处的讨论:http://learningcppisfun.blogspot.com/2009/07/string-literals-in-c.html

强制类型转换完成后,在语法上可以自由地修改文字,但它会失败,因为编译器将文字存储在不可写的内存段中,这是标准本身允许的。

这是在代码段中创建的:

char *a = "abcde";

本质上是const

如果您想编辑它,请尝试:

char a[] = "abcde";

标准规定,无论是否标记为const,都不允许直接修改字符串字量:

是否所有字符串字面值都是不同的(即存储在不重叠的对象)是实现定义的。试图修改字符串字面值的效果是未定义的。

事实上,在C中(不像c++),字符串字面值是而不是 const,但是你仍然不允许写它们。

对写入的限制允许进行某些优化,例如沿着以下行共享字面值:

char *ermsg = "invalid option";
char *okmsg =   "valid option";

,其中okmsg实际上可以指向ermsg中的'v'字符,而不是一个不同的字符串。

字符串通常存储在只读内存中。更改内存将会终止程序。

这里有一个很好的解释:c++中的字符串字面值是在静态内存中创建的吗?

这大多是古代历史;很久以前,字符串字面值不是常量。

然而,大多数现代编译器将字符串文字放入只读内存(通常是程序的文本段,代码也存在于此),任何更改字符串文字的尝试都会产生核心转储或同等内容。

使用g++,您肯定可以获得编译警告(如果默认情况下未启用-Wall)。例如,在MacOS X 10.6.7上编译的g++ 4.6.0(但在10.7上运行)产生:

$ cat xx.cpp
int main()
{
    char* inputStr = "abcde";
   *(inputStr+1)='f';
}
$ g++ -c xx.cpp
xx.cpp: In function ‘int main()’:
xx.cpp:3:22: warning: deprecated conversion from string constant to ‘char*’ [-Wwrite-strings]
$

所以警告在默认情况下是启用的。

编译器将常量"abcde"放在某个只读内存段中。您将(非const) char* inputStr指向该常数,然后"砰"地一声,出现了段错误。

教训:不要调用未定义行为

编辑(细化)

但是,对于第一种情况,在实际进行赋值操作时,没有编译错误,只有分段错误。

你需要启用编译器警告。总是将编译器警告设置得尽可能高。

尽管"abcde"是一个不应该被修改的字符串字面量,但是通过使用一个非const的char*指向它,您已经告诉编译器您不关心它。

编译器会很高兴地假设你知道你在做什么,而不会抛出错误。但是,当您确实尝试修改字符串文字时,代码很有可能在运行时失败。

字符串字量虽然正式是非const,但几乎总是存储在只读内存中。在您的设置中,显然只有当它被声明为const char数组时才会出现这种情况。

用Ritchie的话说一点字符串文字的历史。主要是关于从K& r1开始的字符串文字的起源和演变。希望这可以澄清关于const和string字面值的一两件事。

"来自:Dennis Ritchie主题:Re:历史问题:字符串字面值。日期:1998年6月2日新闻组:comp.std.c

在C89委员会工作的时候,可写的字符串字面量不是"遗留代码"(Margolin)和什么标准存在(K&R 1)非常明确(A.2.5)字符串只是初始化静态数组的一种方式。正如Barry指出的,有一些(mktemp)例程用了这个事实

我没有出席委员会对…的审议点,但我怀疑BSD实用程序为篡改汇编代码移动字符串的初始化以文本代替数据,并意识到大多数字面值字符串实际上并没有被覆盖,而是更多

我认为委员会可能遗漏了什么是找不到一个可以解释的公式用const表示字符串字面值的行为。也就是说,如果"abc"是类型的匿名文字Const char [4]然后是它所有的性质(包括能够使只读,甚至共享其存储与同一文字的其他出现)是几乎解释。

这个问题不仅仅是相对较少的字符串字面量实际写入的位置,但是很多更重要的是,制定可行的分配规则指向const的指针,特别是对于函数的实际指针参数。实际上,委员会知道无论如何他们制定的规则不需要强制性规定现有世界中所有函数("string")的诊断。

所以他们决定保留普通字符数组中的"…"输入,但假设一个人被要求不写在上面。

顺便说一句,这篇笔记并不是想要被理解为一篇短评在C89的配方。很难得到东西正确(连贯和正确)和可用(一致)够了,够吸引人了)。

Dennis

"