以下C char数组存储实现背后的原因是什么?

Ash*_*win 8 c language-design

以下char数组实现背后的实现原因是什么?

char *ch1 = "Hello"; // Read-only data
/* if we try ch1[1] = ch1[2]; 
we will get **Seg fault** since the value is stored in 
the constant code segment */

char ch2[] = "World"; // Read-write data
/* if we try ch2[1] = ch2[2]; will work. */
Run Code Online (Sandbox Code Playgroud)

根据Head first C(第73,74页)一书,ch2[]数组既存储在常量代码段中,也存储在函数堆栈中.在代码和堆栈内存空间中复制的原因是什么?如果不是只读数据,为什么值只能保存在堆栈中?

pax*_*blo 7

首先,让我们澄清一些事情.字符串文字并不一定是只读数据,它只是它是不确定的行为,试图改变它们.

不一定要崩溃,它可能工作得很好.但是,如果您希望代码在另一个实现,同一实现的另一个版本或甚至下周三运行,则不应该依赖它.

这可能源于标准制定之前的时间(最初的ANSI/ISO授权是编纂现有实践而不是创建新语言).在许多实现中,字符串将共享空间以提高效率,例如代码:

char *good = "successful";
char *bad = "unsuccessful";
Run Code Online (Sandbox Code Playgroud)

导致:

good---------+
bad--+       |
     |       |
     V       V
   | u | n | s | u | c | c | e | s | s | f | u | l | \0 |
Run Code Online (Sandbox Code Playgroud)

因此,如果您更改了其中一个字符good,它也会发生变化bad.

您可以使用以下内容执行此操作的原因:

char indifferent[] = "meh";
Run Code Online (Sandbox Code Playgroud)

是,虽然goodbad指向一个字符串,该声明实际上创建了一个字符数组大到足以容纳"meh",然后复制数据到它1.可以自由更改数据的副本.

事实上,C99基本原理文件明确指出这是其中一个原因:

字符串文字不需要是可修改的.此规范允许实现共享具有相同文本的字符串副本,将字符串文字放在只读内存中,并执行某些优化.

但无论为什么,标准都很清楚.从C11 6.4.5 String literals:

7 /如果这些数组的元素具有适当的值,则这些数组是否不同是未指定的.如果程序试图修改此类数组,则行为未定义.

对于后一种情况,这包括在6.7.6 Declarators6.7.9 Initialisation.


1虽然值得注意的是正常的"似乎"规则适用于此(只要实现的行为就像它遵循标准一样,它可以做它喜欢的事情).

换句话说,如果实现可以检测到您从未尝试更改数据,则可以非常愉快地绕过副本并使用原始数据.


edm*_*dmz 3

我们会得到Seg 错误,因为该值存储在常量代码段中

这是错误的:您的程序崩溃是因为它收到一个指示段违规 ( SIGSEGV) 的信号,默认情况下,该信号会导致程序终止。但这不是主要原因。修改字符串文字是未定义的行为,无论它是否存储在只读段中,这比您想象的要广泛得多。

数组既存储在常量代码段中,也存储在函数堆栈中。

这是一个实现细节,您不应该关心:就 ISO C 而言,这些陈述没有任何意义。这也意味着它可以以不同的方式实施。

当你

 char ch2[] = "World";
Run Code Online (Sandbox Code Playgroud)

"World",它是一个字符串文字,被复制到,如果您使用和 指针,ch2您最终会这样做。malloc现在,为什么要复制它?

原因之一可能是这是您所期望的。如果您可以修改此类字符串文字,那么如果代码的另一部分引用它并期望具有该值怎么办?共享字符串文字非常高效,因为您可以在程序中共享它们并节省空间。

通过复制它,您就拥有了自己的字符串副本(您“拥有”它)并且可以随意修改它。

引用《美国信息系统编程语言 C 国家标准的基本原理》

字符串文字被指定为不可修改。该规范允许实现共享具有相同文本的字符串副本,将字符串文字放入只读内存中,并执行某些优化。但是,字符串文字没有 const char 的类型数组,这是为了避免指针类型检查的问题,特别是对于库函数,因为将指向 const char 的指针分配给指向 char 的普通指针是无效的。