为什么修改指向字符串litteral的指针的内容是错误的?

时间:2015-09-28 11:15:07

标签: c++

如果我写:

char *aPtr = "blue"; //would be better const char *aPtr = "blue"
aPtr[0]='A';

我有一个警告。上面的代码可以工作,但不是标准的,它有一个未定义的行为,因为它的只读内存,指针在字符串litteral。问题是: 为什么会这样? 用这段代码代替:

char a[]="blue";
char *aPtr=a;
aPtr[0]='A';

没问题。我想在幕后了解会发生什么

2 个答案:

答案 0 :(得分:1)

第一个是指向由编译器创建并放在程序的只读部分中的只读值的指针。您无法修改该地址的字符,因为它们是只读的。

第二个创建一个数组并复制初始化程序中的每个元素(有关详细信息,请参阅this answer)。您可以修改数组的内容,因为它是一个简单的变量。

第一个按照它的方式工作,因为做其他事情需要动态分配一个新变量,并且需要垃圾收集来释放它。这不是C和C ++的工作方式。

答案 1 :(得分:1)

无法修改字符串文字(没有未定义的行为)的主要原因是支持字符串文字合并。

很久以前,当内存比今天更紧凑时,编译器的作者注意到许多程序都重复了多次相同的字符串文字 - 特别是模式字符串被传递给fopen(例如f = fopen("filename", "r"); })和传递给printf的简单格式字符串(例如,printf("%d\n", a);)。

为了节省内存,他们会避免为这些字符串的每个实例分配单独的内存。相反,他们会分配一个内存,并将所有指针指向它。

在少数情况下,他们甚至比这更棘手,合并甚至完全相同的文字。例如,考虑这样的代码:

printf("%s\t%d\n", a);
/* ... */
printf("%d\n", b);

在这种情况下,字符串文字并不完全相同,但第二个字符串是第一个字符串结尾的相同部分。在这种情况下,他们仍然会分配一块内存。一个指针指向内存的开头,另一个指针指向同一内存块中%d的位置。

有可能(但不要求)字符串文字合并,基本上不可能说出修改字符串文字时会得到什么样的行为。如果合并了字符串文字,则修改一个字符串文字可能会修改相同或相同结尾的其他字符串文字。如果未合并字符串文字,则修改字符串文字将不会影响任何其他文字。

MMU增加了另一个维度:它们允许将内存标记为只读,因此尝试修改字符串文字会产生某种信号 - 但前提是系统有一个MMU(通常是可选的)一次)并且还取决于编译器/链接器是否决定将字符串文字放入内存中,它们是否标记为常量。

由于他们无法定义修改字符串文字时的行为,因此他们决定修改字符串文字会产生未定义的行为。

第二种情况完全不同。在这里,您已经定义了一个char数组。很明显,如果你定义了两个独立的数组,它们仍然是分开的,无论内容如何,​​因此修改一个不会影响另一个。行为是明确的,并且一直都是,所以这样做会给出定义的行为。有问题的数组可能是从字符串文字初始化的事实并没有改变它。