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

时间:2010-06-19 09:38:26

标签: c++ c memory standards

内存中的文字到底在哪里? (见下面的例子)

我无法修改文字,因此它应该是一个const char *,虽然编译器允许我使用char *,即使有大多数编译器标志也没有警告。

尽管将const char *类型隐式转换为char *类型会给我一个警告,请参阅下面的内容(在GCC上测试,但在VC ++ 2010上的行为类似)。

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

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 */

6 个答案:

答案 0 :(得分:11)

C标准不禁止修改字符串文字。它只是表示如果尝试,行为是不确定的。根据C99的基本原理,委员会中有人希望字符串文字可以修改,因此标准没有明确禁止它。

请注意,C ++中的情况有所不同。在C ++中,字符串文字是const char的数组。但是,C ++允许从const char *到char *的转换。不过,该功能已被弃用。

答案 1 :(得分:2)

主要是历史原因。但请记住,它们有些合理:字符串文字没有类型char *,但char [N]其中N表示缓冲区的大小(否则,sizeof不会“在字符串文字上按预期工作”,可用于初始化非const数组。您只能将它们分配给const指针,因为数组隐式转换为指针而非const转换为const

如果字符串文字表现出与复合文字相同的行为,那将更加一致,但由于这些是C99构造并且必须保持向后兼容性,因此这不是一个选项,因此字符串文字保持特殊情况。

答案 2 :(得分:1)

  

内存中的文字到底在哪里? (见下面的例子)

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

  

我无法修改文字,因此它应该是一个const char *,虽然编译器允许我使用char *,即使有大多数编译器标志也没有警告。

历史因为其他人已经解释过了。大多数编译器允许您通过命令行选项判断字符串文字是只读还是可修改。

通常希望将字符串文字设置为只读的原因是内存中具有只读数据的段可以(并且通常)在从可执行文件启动的所有进程之间共享。这显然可以避免浪费一些RAM来保留相同信息的冗余副本。

答案 3 :(得分:1)

我不确定C / C ++标准对字符串的影响。但我可以告诉完全 实际在MSVC中使用字符串文字。而且,我相信,其他编译器的行为也相似。

字符串文字驻留在const数据部分中。它们的内存映射到进程地址空间。但是,它们存储的内存页面是ead-only(除非在运行期间明确修改)。

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

const char* a = "test";

上述语句使编译器创建一个字符串文字“test”。链接器确保它将在可执行文件中。 在函数体中,编译器生成一个代码,该代码在堆栈上声明变量a,该变量由字符串文字“test。”的地址初始化。

char* b = a;

在这里,您在堆栈上声明另一个变量b,其值为a。由于a指向只读地址 - b也是如此。偶然事实b没有const语义并不意味着您可以修改它指向的内容。

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

以上内容会产生访问冲突。同样,缺少const并不代表机器级别的任何内容

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

首先 - 上面与字符串文字无关。 'a'不是字符串。这是一个角色。这只是一个数字。这就像写下面的内容:

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

上面的代码使编译器变得愚蠢。你说变量是const,但你设法修改它。但这与处理器异常无关,因为d仍然驻留在读/写内存中。

我想再添一个案例:

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

上面声明了堆栈上的数组,并使用字符串“test”对其进行初始化。它驻留在读/写内存中,可以修改。这里没问题。

答案 4 :(得分:0)

  

即使有大多数编译器标志,我也没有警告

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

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

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

  

警告:已弃用从字符串常量转换为'char *'

答案 5 :(得分:0)

您可以写信给c,因为您没有将其设为const。将c定义为const将是正确的做法,因为右侧的类型为const char*

它在运行时生成错误,因为“test”值可能分配给只读的代码段。请参阅herehere