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

vit*_*aut 16 c++ string printf iostream volatile

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

#include <cstdio>
#include <iostream>

int main() {
  volatile char test[] = "abc";
  std::printf("%s\n", test);
  std::cout << test << "\n";
}
Run Code Online (Sandbox Code Playgroud)

使用GCC进行编译并运行会提供以下输出:

$ g++ test.cc 
$ ./a.out 
abc
1
Run Code Online (Sandbox Code Playgroud)

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

Mik*_*our 14

唯一合适的重载operator<<是for bool,所以数组被转换(通过指针)bool,true因为它的地址是非空的.1除非您使用std::boolalpha操纵器,否则输出为.

它不能使用const char *输出字符串的重载,或const void *输出指针值的重载,因为这些转换需要删除volatile限定符.隐式指针转换可以添加限定符,但不能删除它们.

要输出字符串,您必须抛弃限定符:

std::cout << const_cast<const char*>(test) << "\n";
Run Code Online (Sandbox Code Playgroud)

但请注意,这会给出未定义的行为,因为数组将被访问,就好像它不是volatile一样.

printf是一种老式的可变功能,没有类型安全.该%s说明符使得它解释说法是const char *,不管它实际上是.

  • 这在技术上是合法的吗?`operator <<`可以假设字符串不是易变的吗?例如,它可能会`strlen(s)`决定分配多大的缓冲区,然后使用`strcpy`复制到该缓冲区. (2认同)
  • 具有强制转换的答案具有未定义的行为:C++ 14 [dcl.type.cv] p6说"如果尝试通过使用具有非易失性的glvalue来引用使用volatile限定类型定义的对象 - 限定类型,程序行为未定义." 通常,您只能通过元素上的手写循环与易失性数组进行交互. (2认同)
  • @ShafikYaghmour:`const_cast`确实产生一个prvalue`conc char*`,指向volatile数组的第一个元素([expr.const.cast]).但这只是意味着_pointer_是一个右值,而不是它所指向的任何东西.取消引用该指针会产生一个引用第一个元素([expr.unary.op])的左值,它会给出未定义的行为. (2认同)

Sha*_*our 5

的std :: basic_ostream ::运算符<<只有一个过载const char*const void*在这种情况下不匹配,因为你不能放弃挥发性没有投预选赛中,这部分内容在草案C++标准的部分4.4 资质转换它说:

如果"cv2 T"比"cv1 T"更符合cv,则可以将类型"指向cv1 T的指针"的prvalue转换为"指向cv2 T的指针"的prvalue.

所以它使用的是bool版本,因为它不是nullptr结果true.

如果从中删除volatile限定符test将提供您期望的结果.几个答案建议使用a const_cast来删除volatile限定符,但这是未定义的行为.我们可以看一下7.1.6.1 cv-qualifiers6段的部分,它说:

如果尝试通过使用具有非volatile限定类型的glvalue来引用使用volatile限定类型定义的对象,则程序行为是未定义的.

const_cast在这种情况下产生一个prvalue但是取消引用该指针会产生一个lvalue,它将调用未定义的行为.