为什么std::cout将易失性指针转换为bool?
如果你尝试cout一个指向易失性类型的指针,即使是一个易失性字符指针,你通常希望cout打印字符串,你只会得到'1'(假设指针不为空,我认为)。我假设输出流运算符<<模板是专门用于易失性指针的,但我的问题是,为什么?什么用例激发了这种行为?
示例代码:
#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
If you try to cout a pointer to a volatile type, even a volatile char pointer where you would normally expect cout to print the string, you will instead simply get '1' (assuming the pointer is not null I think). I assume output stream operator<< is template specialized for volatile pointers, but my question is, why? What use case motivates this behavior?
Example code:
#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;
}
Output:
Hello world
1
0x8046b6c
1
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在 C++20 标准中,
ostream::operator<<
具有以下重载,其中包括:当传入 易失性指针时,无法应用第二个重载,因为易失性指针不能无需显式强制转换即可转换为非易失性。然而,任何指针都可以转换为 bool,所以选择第一个重载,你看到的结果是 1 或 0。
所以真正的原因并不是代表标准委员会的有意决定,而只是标准不指定采用易失性指针的重载。
2023 年更新
从 C++23 标准开始(草案N4944,cppreference),
ostream: :operator<<
添加以下重载:在 C++23 模式下编译时(如果您的编译器支持它),易失性指针现在按照您的预期进行格式化,而不是隐式转换为
布尔
。Up through the C++20 standard,
ostream::operator<<
has the following overloads, among others:When you pass in a volatile pointer, the second overload can't apply because volatile pointers cannot be converted to non-volatile without an explicit cast. However, any pointer can be converted to bool, so the first overload is chosen, and the result you see is 1 or 0.
So the real reason for this is not an intentional decision on behalf of the standards committee, but simply that the standard does not specify an overload that takes a volatile pointer.
2023 Update
Beginning with the C++23 standard (draft N4944, cppreference),
ostream::operator<<
adds the following overload:When compiling in C++23 mode (if your compiler supports it), volatile pointers are now formatted as you'd expect, instead of being implicitly converted to
bool
.我认为原因是易失性指针不能隐式转换为void *。这是标准的附录 C,其基本原理是类型安全。
因此,您不会转换为 void *(将以十六进制打印),而是得到“默认”转换为 bool。
I think the reason is that volatile pointers cannot be converted implicitly to void *. This is in Appendix C of the Standard, and the rationale is type safety.
So instead of the conversion to void * (which would print in hex), you get the "default" conversion to bool.
我认为问题不是指向易失性类型的指针的显式重载,而是指向易失性类型的指针缺乏重载。编译器无法隐式地从指针中删除 volatile 限定符,因此它会检查可用的重载,选择运算符 << 的 bool 版本。并将易失性指针转换为布尔值。
I think the problem is not an explicit overload for pointers to volatile types, but a LACK of overload for pointers to volatile types. The compiler can't implicitly remove the volatile qualifier from the pointers so it checks available overloads, picks the bool version of operator<< and converts the pointer-to-volatile to bool.
不是答案
这只是问题和答案的措辞问题。出现问题的原因是无法将指向易失性对象的指针转换为void指针,而不是易失性指针。
相当重要的区别在于哪个内存元素是易失性的。在问题中,指针不是易失性的(它可以被缓存,并且在更改时不必刷新到内存),而是指向的内存:
它之所以重要的原因是,用易失性指针指向内存中,指针本身是有特殊处理的。
在上面的代码中,标记为 1 和 2 的操作使其一直进入内存。 [1] 中的赋值必须转储到内存中。即使
p
的值在寄存器中,它也会从[2]处的内存加载。标记为[3]的操作修改了q
指向的值,该值是易失性
并且会一直到达主内存,而操作[4]只影响指针,它本身不是易失性的,因此不是 C++ 内存模型可感知状态的一部分,并且可以在寄存器中执行(请注意,编译器可以优化 q 并执行寄存器中的操作,而p
无法优化。Not an answer
This is just an issue with the wording of the question and the answers. The problem arises due to the inability to convert pointers to volatile objects into void pointers, not volatile pointers.
The difference, which is rather important, is what memory element is the one that is volatile. In the question, the pointer is not volatile (it can be cached, and it does not have to be flushed to memory when it is changed), but rather the pointed memory:
The reason why it is important is that with a volatile pointer to memory, the pointer itself is the one that has special treatment.
In the code above, the operations marked as 1 and 2 make it all the way to memory. The assignment in [1] must be dumped to memory. Even if the value of
p
is in a register, it will be loaded from memory at [2]. The operation marked [3] modifies the value pointed byq
which isvolatile
and will make all the way to main memory, while the operation [4] only affects the pointer, which is notvolatile
itself, and as such is not part of the c++ memory model perceivable state and can be performed in registers (note that a compiler can optimize awayq
and perform the operations in a register, whilep
cannot be optimized.