17

如果您尝试 cout 指向 volatile 类型的指针,甚至是您通常期望 cout 打印字符串的 volatile char 指针,您将改为简单地得到 '1' (假设指针不为 null 我认为)。我假设输出流 operator<< 是专门用于 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

4 回答 4

28

ostream::operator<<具有以下重载,其中包括:

ostream& operator<< (bool val );
ostream& operator<< (const void* val );

当您传入一个 volatile 指针时,第二个重载将无法应用,因为 volatile 指针在没有显式转换的情况下无法转换为 non-volatile。但是,任何指针都可以转换为 bool,因此选择了第一个重载,您看到的结果是 1 或 0。

因此,这样做的真正原因不是代表标准委员会的有意决定,而只是标准没有指定采用 volatile 指针的重载。

于 2010-03-23T16:39:31.830 回答
5

我认为原因是 volatile 指针不能隐式转换为 void *。这在标准的附录 C 中,其基本原理是类型安全。

更改:只有指向非常量和非易失性对象的指针可以隐式转换为 void* 理由:这提高了类型安全性。

因此,您无需转换为 void *(将以十六进制打印),而是将“默认”转换为 bool。

于 2010-03-23T16:42:55.000 回答
2

不是答案

这只是问题和答案的措辞问题。问题的出现是由于无法将指向 volatile对象的指针转换为 void 指针,而不是volatile 指针

相当重要的区别在于哪个存储元件是易失性的。在问题中,指针不是易失的(它可以被缓存,并且在更改时不必刷新到内存),而是指向的内存:

int volatile * p = f();
p++;      // this does not affect the perceived state of the c++ memory model
++p;
*p = 5;   // this changes the perceived state

之所以重要,是因为对于指向内存的 volatile 指针,指针本身就是需要特殊处理的指针。

void foo( int * );

int * volatile p = f();  // 1
foo(p);                  // 2
int volatile * q = f();
//foo(q);    // error, cannot convert pointer to volatile to pointer to non-volatile
*q = 5;                  // 3
q = 0;                   // 4

在上面的代码中,标记为 1 和 2 的操作一直到内存。[1] 中的赋值必须转储到内存中。即使 的值p在寄存器中,它也会在 [2] 处从内存中加载。标记为 [3] 的操作修改了指向的值qvolatile并且将一直到主内存,而操作 [4] 只影响指针,它不是volatile自身,因此不是 c++ 内存模型的一部分可感知的状态并且可以在寄存器中执行(请注意,编译器可以优化q并在寄存器中执行操作,而p无法优化。

于 2010-03-23T17:25:52.780 回答
1

我认为问题不是指向 volatile 类型的指针的显式重载,而是指向 volatile 类型的指针缺少重载。编译器无法从指针中隐式删除 volatile 限定符,因此它会检查可用的重载,选择 operator<< 的 bool 版本并将指向 volatile 的指针转换为 bool。

于 2010-03-23T16:46:14.777 回答