cout << 使用char*参数打印字符串,而不是指针值

88

这个:

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

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


这回答解决了你的问题吗?为什么cout打印字符数组与其他数组不同? - Melebius
6个回答

106

这是因为 std::cout 会将 char * 视为指向 C 风格字符串的指针(即第一个字符的指针)并打印出来。如果你想获得地址,可以将其转换为不被视为 C 风格字符串的指针,例如:

cout << (void *) terry;

(如果你担心去掉const会有问题,针对这种情况可以使用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 <void *> 转换掉常量性(这就是 const_cast 的作用)。这是新版 C++ 转换所执行的检查之一,而旧式转换没有此限制。


2
它不将char*值视为C风格字符串;它将其视为指向C风格字符串(第一个字符)的指针 - Keith Thompson
4
对于那些不知道自己在做什么的人来说才是危险的。在这种情况下,取消 const 属性是无关紧要的,因为你没有对指针做任何会影响它的事情。如果你强制类型转换了指针然后使用不当,我同意这是有问题的,但是我期望编程人员学会如何安全地使用语言。就像我不让儿子在他证明了理解危险工具的问题之前开始使用太危险的工具一样 :-) - paxdiablo
4
我只看到在C++中使用C风格的强制类型转换有两个原因:1. 转换到无法访问的基类;2. 懒得多打几个字符。 - gx_
9
你谈论懒惰的时候好像它是一件坏事。只有在实际造成负面影响时才会是不好的,否则就是效率高的表现。在这种特定情况下使用旧式转换没有问题,它们和其他任何C++语言元素一样重要(即使像cstdio这样的东西也是可以接受的,只要你理解其中的问题)。 - paxdiablo
2
然而,我看到我不太可能说服你。纯粹主义者和实用主义者之间的斗争似乎在这里出现了,双方都有他们的观点,但我们可能会讨论vi vs. emacs :-) 所以我建议你按照自己的良心投票,我们让人民来决定。我会添加新的样式作为一个选项,但我仍然认为在这种情况下它是不必要的。 - paxdiablo
显示剩余7条评论

29

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

如果您提供一个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<<打印出操作数值以外内容的情况。这个原因可以追溯到C++源于C的根基,C没有“字符串”类型,通过char*指针来操纵字符串。

对于各种整数和浮点数类型、std::string等,operator<<还有很多其他重载。


我能想到的另一个“特殊”情况是尝试打印函数指针,这将选择bool重载(除非该函数具有流操作符的签名):http://ideone.com/OkutRD(诚然,它打印了指针转换为`bool`的“值”,因此这比`const char*`情况“不太特殊”) - gx_
@KeithThompson 那么这意味着是 operator<<overloaded version 使得字符串被打印而不是指针值,而对于像 int* 这样的情况,指针值将被打印而不是值。我在这里是正确的吗? - ajaysinghnegi

13

你应该将你的代码更改为这样:

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

问题是指针指向C风格字符串的<<运算符被重载,用于打印字符串的内容。如果您改为将其转换为裸指针,则可以使用iostreams以所需的方式打印指针的默认行为。


5

std::cout 被定义为带有 this operator<<std::ostream

值得注意的是这一行:

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

当您使用类型为char*的参数和<<时,将选择此内容。

任何其他非字符指针类型的情况在这里处理:

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

这继续涉及到 std::num_put,它用于格式化数字值。因此,指针像 C 格式化函数中的 %p 一样被数值解释。

2

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

如果你使用printf而不是cout,那么你会看到该地址。你也可以将指针强制转换为另一种类型,比如(void*),那么你也会得到该地址。


3
printf本身并不决定如何打印输出,你仍然需要使用正确的格式说明符,就像在C++中使用相同类型一样。例如,使用%sprintf将与使用char *cout具有完全相同的问题。要获取指针,您可以使用格式说明符%p - paxdiablo

-2
“hello”是一个字符串,即字符数组。const char*是指向该数组的指针,因此当您取消引用此指针时,您将获得第一个元素的值。
这就像如果你有
int a[] = {1, 2, 3};
int *b = a;
cout << *b << endl;

你只会得到打印出来的 1


为什么我没有得到内存地址? - Mr.Puff
1
@Mr.Puff 如果你想要得到 char 指针的内存地址,你应该在之前将它转换为 void 指针,因为 ostream << 运算符被重载用于打印字符串的 char 指针。 - Ivan Smirnov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接