为什么ostream为定义为`volatile char []`的字符串打印`1`?

时间:2014-07-03 13:39:56

标签: c++ string printf iostream volatile

考虑这个(人为的)例子:

#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

正如您所见,printfcout打印1时正确打印字符串。在这种情况下,为什么写cout会产生1

4 个答案:

答案 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";