如果你试图cout一个指向volatile类型的指针,即使是一个volatile char指针,你通常会期望cout打印字符串,你会得到'1'(假设指针不是null)。我假设输出流操作符〈〈是专门针对volatile指针的模板,但我的问题是,为什么?什么用例激发了这种行为?
示例代码:
#include <iostream>
#include <cstring>
int main()
{
char x[500];
std::strcpy(x, "Hello world");
int y;
int *z = &y;
std::cout << x << std::endl;
std::cout << (char volatile*)x << std::endl;
std::cout << z << std::endl;
std::cout << (int volatile*)z << std::endl;
return 0;
}
输出:
Hello world
1
0x8046b6c
1
4条答案
按热度按时间oyt4ldly1#
在C++20标准中,
ostream::operator<<
有以下重载:当传入volatile指针时,第二个重载不能应用,因为volatile指针在没有显式强制转换的情况下无法转换为nonvolatile。然而,任何指针都可以转换为bool,因此选择了第一个重载,您看到的结果是1或0。
因此,真实的的原因并不是代表标准委员会的故意决定,而只是标准没有指定接受volatile指针的重载。
2023更新
从C++23标准(draft N4944,cppreference)开始,
ostream::operator<<
添加了以下重载:当在C++23模式下编译时(如果你的编译器支持它),volatile指针的格式现在和你期望的一样,而不是隐式地转换为
bool
。fjaof16o2#
我认为原因是volatile指针不能隐式转换为void 。这在标准的附录C中,基本原理是类型安全。
更改:只有指向非常量和非易失性对象的指针可以隐式转换为void 理由:这提高了类型安全性。
因此,不是转换为void *(以十六进制打印),而是“默认”转换为bool。
2vuwiymt3#
这只是问题和答案的措辞问题。问题的出现是由于无法将 * 指向volatile* 对象的指针转换为void指针,而不是 *volatile指针 *。
其中的区别非常重要,那就是哪些内存元素是易失性的。在问题中,指针不是易失性的(它可以被缓存,并且当它被改变时不必被刷新到内存中),而是指向的内存:
它之所以重要,是因为对于指向内存的volatile指针,指针本身是具有特殊处理的指针。
在上面的代码中,标记为1和2的操作使其一直到内存。[1]中的赋值必须转储到内存。即使
p
的值在寄存器中,它也会在[2]从内存中加载。标记为[3]的操作修改q
指向的值,即volatile
,并将一直到主内存,而操作[4]只影响指针,它不是volatile
本身,因此不是C++存储器模型可感知状态的一部分,并且可以在寄存器中执行(注意,编译器可以优化掉q
并在寄存器中执行操作,而p
不能被优化。nzkunb0c4#
我认为问题不是指向volatile类型的指针的显式重载,而是指向volatile类型的指针缺少重载。编译器不能隐式地从指针中删除volatile限定符,所以它会检查可用的重载,选择运算符〈〈的bool版本并将指向volatile的指针转换为bool。