为什么编译器允许string文字不是const?

记忆中的文字究竟在哪里? (见下面的例子)

我不能修改一个文字,所以它应该是一个const char *,尽pipe编译器让我使用char *,即使大部分的编译器标志都没有警告。

而char chartypes的const char *types的隐式转换给了我一个警告,见下面(在GCC上testing,但在VC ++ 2010上performance类似)。

另外,如果我修改了一个const char的值(在下面的GCC最好给我一个警告的技巧),它不会给出错误,我甚至可以修改并显示在GCC上(即使我猜测它仍然是一个未定义的行为,我不知道为什么它没有这样做的文字)。 这就是为什么我问这些文字被存储在哪里,以及在哪里存储更常见的常量?

const char* a = "test"; char* b = a; /* warning: initialization discards qualifiers from pointer target type (on gcc), error on VC++2k10 */ char *c = "test"; // no compile errors c[0] = 'p'; /* bus error when execution (we are not supposed to modify const anyway, so why can I and with no errors? And where is the literal stored for I have a "bus error"? I have 'access violation writing' on VC++2010 */ const char d = 'a'; *(char*)&d = 'b'; // no warnings (why not?) printf("%c", d); /* displays 'b' (why doesn't it do the same behavior as modifying a literal? It displays 'a' on VC++2010 */ 

C标准不禁止修改string文字。 它只是说,如果尝试进行,行为是不确定的。 根据C99的理由,委员会中有些人希望string文字可以修改,所以标准没有明确禁止。

请注意,在C ++中的情况是不同的。 在C ++中,string文字是const char的数组。 但是,C ++允许从const char *转换为char *。 但是,该function已被弃用。

主要是历史原因。 但请记住,它们有些合理:string文字不具有char *types,但是char [N]其中N表示缓冲区的大小)(否则, sizeof在string文本中不能按预期工作),并且可以是用于初始化非常量数组。 您只能将它们分配给const指针,因为数组隐式转换为指针,而非常量指向const

如果string文字performance出与复合文字相同的行为,则会更加一致,但是由于它们是C99构造,并且必须保持向后兼容性,所以这不是一个选项,所以string文字保持特殊情况。

记忆中的文字究竟在哪里? (见下面的例子)

初始化数据段。 在Linux上,它是.data.rodata

我不能修改一个文字,所以它应该是一个const char *,尽pipe编译器让我使用char *,即使大部分的编译器标志都没有警告。

历史已经被别人解释了。 大多数编译器允许您通过命令行选项来判断string文本是只读还是可以修改。

通常希望string文字是只读的原因是内存中只读数据段可以(并且通常)在从可执行文件开始的所有进程之间共享。 这显然释放了一些内存不被浪费,以保持相同信息的冗余副本。

我不确定C / C ++标准对于string的含义。 但是我可以确切地知道MSVC中string文字实际发生了什么。 而且,我相信其他编译器的行为也是相似的。

string文字驻留在常量数据部分。 他们的内存映射到进程地址空间。 但是,它们存储的内存页面是纯粹的(除非在运行期间明确修改)。

但还有更多你应该知道的东西。 并非所有包含引号的C / C ++expression式都具有相同的含义。 让我们澄清一切。

 const char* a = "test"; 

上面的语句使得编译器创build一个string文字“test”。 链接器确保它将在可执行文件中。 在函数体中,编译器生成一个代码,在栈上声明一个variablesa ,它由string文字“test”的地址初始化。

 char* b = a; 

这里你在栈上声明另一个variablesb ,它获得a的值。 由于指向一个只读地址 – 所以会b 。 偶数的事实b没有const语义并不意味着你可以修改它指向的东西。

 char *c = "test"; // no compile errors c[0] = 'p'; 

上述生成访问冲突。 同样, const的缺乏在机器级别上并不意味着什么

 const char d = 'a'; *(char*)&d = 'b'; 

首先 – 以上与string文字无关。 'a'不是一个string。 这是一个angular色。 这只是一个数字。 这就像写下以下内容:

 const int d = 55; *(int*)&d = 56; 

上面的代码使得编译器无法工作。 你说这个variables是const ,但你可以修改它。 但是这与处理器exception无关,因为d驻留在读/写存储器中。

我想补充一个案例:

 char b[] = "test"; b[2] = 'o'; 

上面在栈上声明了一个数组,并用string“test”初始化它。 它驻留在读/写存储器中,并且可以被修改。 这里没有问题。

即使大部分编译器标志都没有警告

真? 当我编译下面的代码片段时:

 int main() { char* p = "some literal"; } 

在g ++ 4.5.0上, 即使没有任何标志 ,我得到以下警告:

警告:不推荐将string常量转换为“char *”

你可以写给c因为你没有使它成为const。 定义c为const将是正确的做法,因为右侧的types为const char*

它在运行时会产生错误,因为“test”值可能被分配给只读的代码段。 看到这里和这里 。