【问题标题】:cout << with char* argument prints string, not pointer valuecout << 带有 char* 参数打印字符串,而不是指针值
【发布时间】:2013-07-22 17:07:45
【问题描述】:

这个:

const char * terry = "hello";
cout<<terry;

打印hello 而不是'h' 的内存地址。为什么会这样?

【问题讨论】:

标签: c++


【解决方案1】:

原因是std::coutchar * 视为指向C 样式字符串(的第一个字符)的指针并按原样打印。如果你想要 address 而不是,你可以将它转换为一个 那样处理的指针,例如:

cout << (void *) terry;

(如果您担心抛弃 constness,则可以使用 const void * 演员表,在这种特殊情况下这不是问题)。


如果您是纯粹主义者而不是实用主义者,您还可以使用 C++ static_cast,如下所示:

cout << static_cast <const void *> (terry);

虽然在这种特殊情况下没有必要,但转换为 void * 可以正常工作。以下示例代码显示了所有这些选项的实际作用:

#include <iostream>
int main (void) {
    const char *terry = "hello";
    std::cout << terry << '\n';
    std::cout << (void *) terry << '\n';
    std::cout << (const void *) terry << '\n';
    std::cout << static_cast<const void *> (terry) << '\n';
    return 0;
}

正在输出(地址在你的环境中可能不同):

hello
0x8048870
0x8048870
0x8048870

请注意,在使用static_cast 时,您应该确保不要尝试用static_cast &lt;void *&gt; 抛弃常量(这就是const_cast 的用途)。这是较新的 C++ 转换所做的检查之一,而旧式转换没有此限制。

【讨论】:

  • 它不会将char* 值视为C 风格的字符串;它将其视为指向 C 样式字符串(的第一个字符)的 指针
  • 这只对那些不知道自己在做什么的人来说是危险的。在这种情况下,删除常量是无关紧要的,因为您没有对重要的指针做任何事情。是的,如果你投掷指针然后消失并错误地使用它,我会同意,但我希望语言的从业者学习 hozw 以安全地使用它。我没有让我儿子开始使用我更危险的工具,直到他证明他知道这些工具的问题:-)
  • 我只看到在 C++ 中使用 C 风格转换的两个原因: 1. 转换为不可访问的基类; 2. 懒得再打几个字。
  • 你把懒惰说成是一件的事。如果有实际的递减效果,那就不好了,否则它被称为效率。在 这种特殊情况 情况下使用旧式强制转换没有问题,并且它们与其他任何语言一样都是 C++ 语言的一部分(只要您了解这些问题,即使 cstdio 之类的东西也是可以接受的)。
  • 但是,我知道我不太可能说服你。纯粹主义者和实用主义者之间的战斗似乎在这里抬头,双方都有自己的观点,但我们不妨讨论一下 vi 与 emacs :-) 所以我建议你投票给你的良心,我们会让人们决定。我将添加较新的样式作为选项,但我仍然认为在这种情况下没有必要。
【解决方案2】:

std::cout 上的 &lt;&lt; 运算符重载。它的行为取决于右操作数的类型。 (实际上是几个不同的函数,都命名为operator&lt;&lt;;编译器决定调用哪一个。)

如果您给它一个char*const char*,它会将操作数视为指向C 样式字符串(的第一个字符)的指针,并打印该字符串的内容:

const char * terry = "hello";
cout << terry; // prints "hello"

如果你给它一个char 值,它会将该值打印为一个字符:

cout << *terry;   // prints "h"
cout << terry[0]; // the same

如果你给它一个void* 类型的指针,它会打印那个指针值(以某种实现定义的方式,通常是十六进制):

cout << static_cast<const void*>(terry); // prints something like 0x4008e4

char*const char* 视为指向C 样式字符串的指针是一种特殊情况,并且唯一(我能想到的)导致operator&lt;&lt; 打印的值不是操作数。原因可以追溯到 C++ 在 C 中的根源,它没有“字符串”类型,而是通过 char* 指针来操作字符串。

operator&lt;&lt;、各种整数和浮点数值类型、std::string 等还有许多其他重载。

【讨论】:

  • 我能想到的另一个“特殊”情况是尝试打印一个函数指针,它将选择bool重载(除非该函数具有流操纵器的签名):ideone.com/OkutRD(诚然,它会打印转换为bool 的指针的“值”,所以这比const char* 的情况“不那么特别”)
  • @KeithThompson 所以,这意味着是operator&lt;&lt;overloaded version 使字符串被打印,而不是指针值,而在类似int* 的情况下,指针值将被打印而不是价值。我说的对吗?
【解决方案3】:

你应该把你的代码改成这样:

cout << static_cast<const void*>(terry);

问题在于 &lt;&lt; 运算符被重载,用于指向 C 样式字符串的指针以打印字符串的内容。如果您将其强制转换为原始指针,您将拥有使用 iostreams 打印指针的默认行为。

【讨论】:

    【解决方案4】:

    std::cout 定义为 std::ostreamthis 定义为 operator&lt;&lt;

    尤其是这一行:

    template< class CharT, class Traits >
    basic_ostream<CharT,Traits>& operator<<( basic_ostream<CharT,Traits>& os,
                                         const char* s );
    

    当您将&lt;&lt;char* 类型的参数一起使用时,将被选中。

    任何其他非字符指针类型的大小写为here

    basic_ostream& operator<<( const void* value );
    

    这继续到std::num_put,它用于格式化数值。因此,指针在 C 格式化函数中的数字解释类似于 %p

    【讨论】:

      【解决方案5】:

      cout 被重载,所以当你给它一个char* 时,它会打印为一个指向 C 风格字符串的指针。因此,它会打印出字符,直到遇到空终止字符。

      如果您使用printf 而不是cout,您会看到地址。您还可以将指针转换为另一种类型,例如(void*),您还可以获得地址。

      【讨论】:

      • printf 本身并不决定如何打印,您仍然必须使用正确的格式说明符,就像在 C++ 中必须使用相同的类型一样。例如,printf%scoutchar * 的问题完全相同。要获取指针,您可以使用格式说明符 %p
      【解决方案6】:

      "hello" 是一个字符串,即 char 数组。 const char* 是一个指向这个数组的指针,所以当你取消引用这个指针时,你会得到第一个元素的值。

      如果你有

      int a[] = {1, 2, 3};
      int *b = a;
      cout << *b << endl;
      

      你只会得到1 打印出来。

      【讨论】:

      • 为什么我没有得到内存地址?
      猜你喜欢
      • 2020-08-09
      • 2014-05-17
      相关资源
      最近更新 更多