2010-03-23 12 views
13

Si intenta utilizar un puntero a un tipo volátil, incluso un puntero de carácter volátil donde normalmente esperaría que cout imprima la cadena, simplemente obtendrá '1' (asumiendo que el puntero no es nulo, creo). Supongo que el operador de flujo de salida < < es una plantilla especializada para punteros volátiles, pero mi pregunta es, ¿por qué? ¿Qué caso de uso motiva este comportamiento?¿Por qué std :: cout convierte los punteros volátiles en bool?

código Ejemplo:

#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; 
} 

Salida:

Hello world 
1 
0x8046b6c 
1 

Respuesta

24

ostream::operator<< tiene las siguientes sobrecargas, entre otros:

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

Cuando se pasa un puntero volátil, la segunda sobrecarga no se puede aplicar porque los punteros volátiles no se pueden convertir a no volátiles sin una lanzamiento explícito. Sin embargo, cualquier puntero se puede convertir a bool, por lo que se elige la primera sobrecarga y el resultado que ve es 1 o 0.

Así que la verdadera razón para esto no es una decisión intencional en nombre del comité de estándares, sino simplemente que el estándar no especifica una sobrecarga que toma un puntero volátil.

+0

+1 más precisamente un puntero a la memoria volátil, un puntero volátil sería 'char * volátil' en lugar de' char volatile * ' –

+0

Por supuesto, puede agregar una función libre sobrecarga' operador << (ostream & os, volátil void * p) {return os << const_cast (p);} 'y terminarlo. – Potatoswatter

+0

@Potatoswatter Eliminar la volatilidad del objeto apuntado para acceder al objeto como si no fuera volátil [es un comportamiento indefinido] (https://stackoverflow.com/a/24555400/1038860), desafortunadamente. (A menos que el objeto apuntado [no fue originalmente] (https://stackoverflow.com/a/7368038/1038860) volátil.) –

5

Creo que la razón es que los punteros volátiles no pueden convertirse implícitamente en vacíos *. Esto está en el Apéndice C de la Norma, y ​​la razón es seguridad de tipo.

Cambio: Solamente los punteros a no constante y objetos no volátiles pueden ser implícitamente convertidos a void * Justificación: Esta mejora la seguridad de tipos.

De modo que en lugar de la conversión a void * (que se imprimiría en hexadecimal), se obtiene la conversión "predeterminada" a bool.

+0

+1, la primera frase debe decir "punteros a volátiles" en lugar de "puntero volátil" :) –

+0

Esa adición de apéndice siempre me hizo preguntarme. Como en C tampoco puede convertir 'T const *' a 'void *'. La última vez que lo busqué, necesitas un yeso también allí. –

1

Creo que el problema no es una sobrecarga explícita para los punteros a los tipos volátiles, sino una FALTA de sobrecarga para los punteros a los tipos volátiles. El compilador no puede eliminar implícitamente el calificador volátil de los indicadores para que compruebe las sobrecargas disponibles, elija la versión bool del operador < < y convierta el puntero a volátil en bool.

+0

De nuevo, esto debería decir "punteros a tipos de objetos volátiles". La diferencia es crucial: 'void f (int * p); int main() {int x = 5; int * volátil p = & x; f (p); } 'la volatilidad del puntero no afecta realmente a la llamada: se leerá y se copiará volátilmente al argumento de la función y se pasará así a' f' (con toda justicia te votaré ya que todos las otras respuestas tienen el mismo problema, incluso si agregan algo más ...) –

2

No es una respuesta

Esto es sólo un problema con el enunciado de la pregunta y las respuestas. El problema surge debido a la imposibilidad de convertir punteros a objetos volátiles en punteros vacíos, no punteros volátiles.

La diferencia, que es bastante importante, es qué elemento de memoria es el que es volátil. En la pregunta, el puntero no es volátil (que puede ser almacenado en caché, y no tiene que ser enviado a la memoria cuando se cambia), sino más bien la memoria en punta:

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 

La razón por qué es importante es que con un puntero volátil a la memoria, el puntero es el que tiene un tratamiento especial.

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 

En el código anterior, las operaciones marcadas como 1 y 2 llegan hasta la memoria. La asignación en [1] debe abandonarse a la memoria. Incluso si el valor de p está en un registro, se cargará desde la memoria en [2]. La operación marcada [3] modifica el valor apuntado por q que es volatile y llega hasta la memoria principal, mientras que la operación [4] solo afecta al puntero, que no es volatile, y como tal no es parte del C++ modelo de memoria perceptible y se puede realizar en registros (tenga en cuenta que un compilador puede optimizar q y realizar las operaciones en un registro, mientras que p no se puede optimizar.

+0

Tienes razón, disculpa por mi pobre redacción. Arreglaré mi publicación. –

Cuestiones relacionadas