【问题标题】:Bug in Visual Studio C++ compiler?Visual Studio C++ 编译器中的错误?
【发布时间】:2010-10-09 08:35:15
【问题描述】:

这段代码在 MS Visual Studio 中的行为很奇怪:

char *s = "hello";
s[0] = 'a';
printf(s); 

在启用优化的发布版本中,它会忽略 s[0] = 'a' 并打印“hello”。如果没有优化或调试版本,它会因访问冲突而崩溃。
这种行为是否符合 C++ 标准?在我看来,编译器应该只允许对字符串文字的常量引用,即

const char *s = "hello";

编辑:我知道为什么会这样,但我不明白为什么允许我对只读内存进行非 const 引用。

【问题讨论】:

  • 其他人认为字符串在只读内存中是正确的,但我很惊讶当您尝试覆盖只读字符串时程序不会崩溃。
  • 为什么每个人都认为他们理解事物的失败构成了事物的错误?
  • 在 asm 列表中,我看到优化器只是丢弃 s[0] = 'a' 并直接调用 printf("hello")。
  • 哇,真的吗?这听起来像是一个编译器错误,它会抛出这样的非平凡代码。

标签: c++ visual-studio visual-c++ standards-compliance


【解决方案1】:

不,这不是编译器中的错误。当你写:

char* s = "hello";

字符串常量"hello" 将被放置在只读部分中,如果您尝试修改它应该会产生异常。 (操作系统异常,而不是 C++ 异常)。

要使其可写,您必须使用数组:

char s[] = { 'h', 'e', 'l', 'l', 'o', 0 };

或者,如果你真的需要一个指针,让它指向一个数组:

char _s[] = { 'h', 'e', 'l', 'l', 'o', 0 };
char* s = _s;

我可以理解您关于只允许使用字符串文字初始化 const 指针的观点,但我认为这会破坏现有代码的很多

【讨论】:

  • char s[] = "Hallo";就够了,不用把字符串字面量拼成数组!
  • 其实对于N个字符的字符串:两者都有效即char s[ N ] = "12...N" and char s[ N + 1 ] = "12...N ";
  • @Konrad - 这是真的。我忘记了你可以这样做。
【解决方案2】:

首先允许此代码(而不是要求声明为char const* 类型)的原因是向后兼容旧的 C 代码。

不过,大多数处于严格模式的现代编译器都会对上述代码发出警告

【讨论】:

  • 我使用了具有最高警告级别的 Visual Studio 2008 express。它没有显示警告:(
  • @zuranthus - 编译器可能发出警告,但不是必须的。不幸的是,将 const 字符串数据分配给非 const 变量是如此普遍(这就是为什么它首先被允许的原因),在任何体面的代码体中都可能会出现大量警告。
  • @zuranthus - 不幸的是,“最大”级别 (/W4) 并未显示所有警告。如果您还没有,请尝试使用 /Wall。
  • @Ferruccio - 不,仍然没有,即使使用 /Wall。
  • 这是我不喜欢 msvc 的地方。它将 C 和 C++ 及其扩展混合在一起。一个人永远不知道何时以严格或非严格模式编译。例如,您必须启用 W4,以便在执行 string &s = string(); 时收到警告(这是无效的 c++)我在 msdn belog 中读到。
【解决方案3】:

我认为 C++ 编译器可以按照标准在只读内存页中分配字符串文字。

【讨论】:

    【解决方案4】:

    这是行不通的,因为 *s 指向一个字符串常量的内存地址,你不能改变它。

    实际上,我有点惊讶你在使用优化编译时没有遇到访问冲突。

    【讨论】:

    • 我认为优化器将 printf(s) 简化为 printf("hello"),因为 s 指向只读内存。然后优化器将 s[0] = 'a' 作为不必要的操作丢弃,因为它不会影响任何东西。
    【解决方案5】:
    char *s = "foo";
    

    是一匹狡猾的小马。它并没有告诉您这确实是一个只读字符串,而实际上它是。之所以如此,是因为,您可以让您的同事编写另一个字符串,例如:

    char *t = "foo";
    

    现在,编译器在其最大的帮助下只会保留一个副本,而更改一个副本将意味着大量工作,只是为了让您和您的朋友满意。因此,它不会尝试这样做。这是您应该在标准中找到的内容。猜猜看,你在做什么会调用 UB。

    也就是说,如果你按照自己的方式行事,就会破坏许多标准委员会的可怜人负担不起的遗留代码。所以,你来了。

    记住const 我们无忧无虑的使用不是一天产生的。 Bjarne 做了很多反省,很多其他人也做了很多,是否把它放进去。事实上,他有一个很好的主意,即拥有只读和只写变量……但我会把这个故事留到另一天。

    最后,还有我们需要照顾的好朋友 C。所以...

    【讨论】:

      【解决方案6】:

      正如其他人所说,您不能修改字符串文字。我还想知道为什么您的代码中没有编译器警告。编译器可以肯定地确定您正在尝试写入只读内存。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-03-22
        • 2019-02-19
        • 1970-01-01
        • 2019-07-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多