views:

233

answers:

4

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
+13  A: 

ostream::operator<< has the following overloads, among others:

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

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 committe, but simply that the standard does not specify an overload that takes a volatile pointer.

interjay
+1 more precisely a pointer to volatile memory, a volatile pointer would be `char * volatile` instead of `char volatile *`
David Rodríguez - dribeas
Of course, you can add a free function overload `operator<<(ostream }` and be through with it.
Potatoswatter
+3  A: 

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.

Change: Only pointers to non-const and non-volatile objects may be implicitly converted to void* Rationale: This improves type safety.

So instead of the conversion to void * (which would print in hex), you get the "default" conversion to bool.

anon
+1, the first sentence should read 'pointers to volatile' instead of 'volatile pointer' :)
David Rodríguez - dribeas
That appendix addition always made me wonder. Since in C you cannot convert `T const*` to `void*` either. Last time i looked it up, you need a cast too there.
Johannes Schaub - litb
+1  A: 

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.

Mark B
Again, this should read 'pointers to volatile object types'. The difference is crucial: `void f( int * p ); int main() { int x = 5; int * volatile p = f(p); }` the volatile-ness of the pointer does not really affect the call: it will volatile-ly be read and copied to the argument of the function and will be thus passed to `f` (in all fairness I will vote you since all the other answers do have the same problem --even if they add something else...)
David Rodríguez - dribeas
+2  A: 

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:

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

The reason why it is important is that with a volatile pointer to memory, the pointer itself is the one that has special treatment.

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

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 by q which is volatile and will make all the way to main memory, while the operation [4] only affects the pointer, which is not volatile 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 away q and perform the operations in a register, while p cannot be optimized.

David Rodríguez - dribeas
You're right, sorry for my poor phrasing, I'll fix my post.
Joseph Garvin