考虑这个(人为的)例子:
#include <cstdio>
#include <iostream>
int main() {
volatile char test[] = "abc";
std::printf("%s\n", test);
std::cout << test << "\n";
}
使用GCC进行编译并运行会提供以下输出:
$ g++ test.cc
$ ./a.out
abc
1
正如您所见,printf
在cout
打印1
时正确打印字符串。在这种情况下,为什么写cout
会产生1
?
答案 0 :(得分:14)
operator<<
唯一合适的重载是bool
的重载,因此数组被转换(通过指针)到bool
,因为它的地址是非true
-空值。除非您使用1
操纵器,否则输出为std::boolalpha
。
它不能使用输出字符串的const char *
的重载,或输出指针值的const void *
的重载,因为这些转换需要删除{{1}限定符。隐式指针转换可以添加限定符,但不能删除它们。
要输出字符串,您必须抛弃限定符:
volatile
但请注意,这会产生未定义的行为,因为数组将被访问,就像它不是易失性一样。
std::cout << const_cast<const char*>(test) << "\n";
是一种老式的变量函数,不提供类型安全性。 printf
说明符使其将参数解释为%s
,无论它实际是什么。
答案 1 :(得分:5)
const char*
只有const void*
或4.4
的重载,在这种情况下不匹配,因为你不能在没有强制转换的情况下丢弃 volatile 限定符,std::basic_ostream::operator<<部分bool
资格转换中包含以下内容:
“指向cv1 T的指针”类型的prvalue可以转换为prvalue 如果“cv2 T”比“cv1 T”更符合cv,则键入“指向cv2 T的指针”。
所以它使用nullptr
版本,因为它不是true
,结果是test
。
如果您从const_cast
删除 volatile 限定符,则会提供您期望的结果。有几个答案建议使用7.1.6.1
来删除volatile限定符,但这是未定义的行为。我们可以通过转到const_cast
cv-qualifiers 段 6 来查看:
在这种情况下,如果尝试引用用a定义的对象 通过使用带glvalue的volatile限定类型 非易失性限定类型,程序行为未定义。
{{1}}会产生一个 prvalue ,但取消引用该指针会产生一个 lvalue ,它将调用未定义的行为。
答案 2 :(得分:2)
通过最少量的网络搜索找到here的答案:
简短回答:
cout
由于bool
限定符而将对象解释为volatile
。这是<<
运算符重载的一个怪癖。长答案:如果没有显式强制转换,则无法将易失性指针转换为非易失性指针,因此
char*
时void*
和<<
重载都不能使用}运算符被调用。没有volatile限定的重载,最接近的匹配是bool
重载,因此您的数组被解释为布尔值而不是地址或字符串。您可以通过多种方式修复它,但显式演员可能是您想要的:
std::cout<< (char*)test <<std::endl;
(我个人会转向const char*
。)
答案 3 :(得分:0)
这是volatile
限定符,将其转换为bool
,请尝试:
std::cout << const_cast<char*>(test) << "\n";