【问题标题】:Why does cout << &r give different output than cout << (void*)&r?为什么 cout << &r 给出的输出与 cout << (void*)&r 不同?
【发布时间】:2014-01-06 20:20:10
【问题描述】:

这可能是一个愚蠢的问题,但我是 C++ 新手,所以我仍然在玩弄基础知识。测试指针,我碰到了一些没有产生我预期的输出的东西。

当我运行以下内容时:

char r ('m');
cout << r << endl;
cout << &r << endl;
cout << (void*)&r << endl;

我预料到了:

m
0042FC0F
0042FC0F

..但我明白了:

m
m╠╠╠╠ôNh│hⁿB
0042FC0F

我在想也许因为r 是char 类型,cout 会将&amp;r 解释为char* 并且[出于某种原因] 输出指针值 - 包含@ 地址的字节987654329@ - 作为一系列chars,但是为什么第一个会是m,指向的地址的内容,而不是第一个字节的char表示指针地址.. 就好像cout 将&amp;r 解释为r 但不仅仅是输出'm',而是继续输出更多字符——从后续11 个内存地址的字节值解释。 。 为什么?为什么是 11?

我在 64 位 Win7 上使用 MSVC++ (Visual Studio 2013)。


后记:我在这里得到了很多正确的答案(正如预期的那样,考虑到问题的琐碎性)。由于我只能接受一个,所以我把它作为我看到的第一个。不过谢谢大家。

所以总结和扩展我的问题中提到的本能理论:

是的,cout 确实将&amp;r 解释为char*,但由于char* 在C++ 中是一个“特殊事物”,它本质上意味着一个空终止字符串(而不是一个指针 [指向单个char]),cout 将尝试通过输出字符(从r 的内存地址的字节内容解释)来打印该字符串,直到遇到@ 987654344@。这解释了额外的 11 个字符(巧合的是,它又多花了 11 个字节来达到NUL)。

为了完整性 - 相同的代码,但使用 int 而不是 char,按预期执行:

int s (3);
cout << s << endl;
cout << &s << endl;
cout << (void*)&s << endl;

生产:

3
002AF940
002AF940

【问题讨论】:

  • char* 被解释为字符串。

标签: c++ visual-c++ pointers char cout


【解决方案1】:

char * 是C++ 中的一个特殊事物,继承自C。在大多数情况下,它是一个C 样式的字符串。它应该指向chars 的数组,以0(NUL 字符,'\0')结尾。

所以它试图打印这个,在'm'之后进入内存,寻找一个终止'\0'。这使它打印一些随机垃圾。这被称为Undefined Behaviour。

【讨论】:

  • 在这种特定情况下,使用此平台上的此编译器,它会打印随机垃圾。它也可能会崩溃、似乎可以正常工作、播放 Andy Griffith 主题或做任何其他十亿件事。未定义就是未定义。 :)
【解决方案2】:

有一个operator&lt;&lt; 重载专门用于char* 字符串。这会输出以 null 结尾的字符串,而不是地址。由于您传递给这个重载的指针不是一个以空值结尾的字符串,所以当operator&lt;&lt; 运行超过缓冲区的末尾时,您也会得到未定义的行为。

相反,void* 重载将打印地址。

【讨论】:

    【解决方案3】:

    因为operator&lt;&lt; 是根据数据类型重载的。

    如果你给它一个char,它假定你想要那个字符。

    如果你给它一个void*,它假定你想要一个地址。

    然而,如果你给它一个char*,它会把它当作一个C风格的字符串并尝试这样输出它。由于 C++ 的初衷是“C with classes”,因此处理 C 风格的字符串是必要的。

    你最后得到所有垃圾的原因仅仅是因为,尽管你向编译器断言,它不是实际上是一个 C 风格的字符串。具体来说,不能保证最后有一个以字符串结尾的 NUL 字符,因此输出例程只会输出它之后发生在内存中的任何内容。

    这可能有效(如果那里有 NUL),它可能会打印乱码(如果附近有 NUL),或者它可能会严重崩溃(如果在它无法读取内存之前没有 NUL)。这不是你应该依赖的东西。

    【讨论】:

      【解决方案4】:

      因为operator&lt;&lt; 的重载将const char 指针作为第二个参数并打印出一个字符串。采用void 指针的重载仅打印地址。

      【讨论】:

        【解决方案5】:

        char * 通常——通常甚至是——指向 C 风格以空字符结尾的字符串(或字符串文字)的指针,并被 ostreams 视为此类。相比之下,void * 明确表示需要一个指针值。

        【讨论】:

          【解决方案6】:

          char const* 和 void const* 的输出运算符 (operator&lt;&lt;()) 被重载。当传递char* 时,char const* 的重载是更好的匹配和选择。此重载需要一个指向以空结尾的字符串开头的指针。你给它一个指向个人char 的指针,即你得到未定义的行为。

          如果你想尝试一个定义明确的例子,你可以使用

          char s[] = { 'm', 0 };
          std::cout << s[0] << '\n';
          std::cout << &s[0] << '\n';
          std::cout << static_cast<void*>(&s[0]) << '\n';
          

          【讨论】:

            猜你喜欢
            • 2021-12-14
            • 1970-01-01
            • 1970-01-01
            • 2015-02-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多