【问题标题】:cout << with char* argument prints string, not pointer valuecout << with char* 参数打印字符串,而不是指针值
【发布时间】:2020-08-09 03:06:23
【问题描述】:

这个:

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

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

【问题讨论】:

标签: c++


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

    如果你有

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

    你只会得到1 打印出来。

    【讨论】:

    • 为什么我没有得到内存地址?
    【解决方案3】:

    原因是std::cout 将char * 视为指向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 :-) 所以我建议你投票给你的良心,我们会让人们决定。我将添加较新的样式作为选项,但我仍然认为在这种情况下没有必要。
    【解决方案4】:

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

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

    【讨论】:

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

    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* 的情况下,指针值将被打印而不是价值。我说的对吗?
    【解决方案6】:

    std::cout 定义为 std::ostream,this 定义为 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。

    【讨论】:

      猜你喜欢
      • 2013-07-22
      • 2014-05-17
      相关资源
      最近更新 更多