2012-04-15 6 views
7

Encontré este código en una entrevista.Explicit casting de ptr a "ptr to a ptr"

int main() 
{ 
    int **p; 
    p = (int **) new int(7); 
    cout<<*p; 
    return 0; 
} 

Esperaba un error de tiempo de ejecución en * p. Pero cuando ejecuté el código, se ejecutó con éxito con la salida "0x7". ¿Alguien puede explicarme cómo funciona esto? Gracias.

+1

¿Qué estás tratando de hacer? Una regla básica es que si tienes que lanzar, lo estás haciendo mal. Como 'new int (7)' devuelve 'int *', necesitabas lanzar. Pero el casting no hace que el código sea correcto. Solo documentos del hecho de que te equivocaste. Retire el yeso y trabaje desde allí. –

+0

@David: el código es quizás de entrevista, no de OP. – Vlad

+0

@ David Heffernan: Esta es una pregunta de entrevista en un examen escrito. Hubo cuatro opciones de error de tiempo de compilación, error de tiempo de ejecución, código se ejecutará bien y ninguno de los anteriores. Incluso yo no entiendo qué tipo de conocimiento están tratando de medir por tales preguntas de entrevista. – CppLearner

Respuesta

5

La respuesta correcta sería Ninguna de las anteriores a menos que tenga algunas restricciones adicionales. Básicamente, el código asigna un int e interpreta esa memoria como si fuera int* (a través de reinterpret_cast). El primer problema es que, al ser un reinterpret_cast, el resultado es no especificado en el caso general, y si el tamaño de int es más pequeño que el tamaño de int* (creo una arquitectura de 64 bits), el resultado es comportamiento indefinido como eres leyendo más allá del tamaño asignado en la llamada new.

+0

Cuando convierte un int (32 bit) en un int * (64 bit) el resultado se extiende a 64 bit. – gulyan

+0

@gulyan Pero en este caso, el compilador no sabe que debería hacer ese tipo de conversión. ¡El único elenco que tiene lugar es de int * a int **, en la línea de arriba! –

+1

@gulyan: No, en un 'static_cast' de un entero de 32 bits a un entero de 64 bits, el compilador realizará la conversión extendiendo el tamaño, pero en un' reinterpret_cast' como es el caso, simplemente agregará el código a * reinterpretar * la memoria. Es decir, obtendrá el puntero al 'int' y leerá los 64 bits siguientes (8 bytes) como si fuera un' int * ', y ese es un comportamiento indefinido. Ese es uno de los mayores problemas con los moldes de estilo C, no es tan claro como los moldes explícitos de C++ –

4

Se crea una nueva int e inicializa con el valor 7.

int *x = new int(7); 

Usted que echarlo a un puntero (por ejemplo, dirección de memoria 7 o 0x07)

int **p = (int**) new int(7); 

A continuación, muestran esta dirección con cout.

*p is equal to (int*)7 

Es un puntero con el valor 7.

+2

Esto parece indicar que el código es correcto o que, al menos, producirá el mismo resultado en cada situación, mientras que no es así. La desreferencia de 'p' causa un comportamiento indefinido. –

0
new int(7); 

asigna memoria para un int cuyo valor es 7 y devuelve un puntero a ella.

int **p = (int **) new int(7); 

le dice al compilador de interpretar que la memoria como un int**.

cout << *p; 

le dice al compilador que en la dirección *p es un int * y para dar salida a su valor. El valor es 0x07. (trata el int 7 como una dirección). Una desreferencia adicional daría lugar a un bloqueo (bien, un comportamiento indefinido para ser precisos).

+3

El código tal como está es un comportamiento indefinido ... no es necesario realizar una desreferencia adicional. –

+0

@Luchian Grigore: Gracias, es útil – CppLearner

0
int main() 
{ 
    int **p;     // declare pointer to pointer called p 
    p = (int **) new int(7); // new allocates integer initialized to value of 7 and returns a pointer. Cast the pointer to a point to pointer. p now represents a pointer to a pointer with contents of 0x7. If you called **p you would get the contents at address 0x7. 
    cout << *p; // dereference p, this yields a pointer, which is an int of value 0x7. 
} 

Probablemente la pregunta tenía como objetivo poner a prueba su conocimiento de los punteros, pero no parece muy práctico.

0
int main() 
{ 
    int **p = (int **) new int(7); 
    cout << *p; 
} 

Así, new int(7) asigna sizeof(int) bytes de memoria y devuelve un puntero a ella. Digamos que la memoria pasa a estar en la dirección X. Luego, X se convierte en (int**) y se almacena en p.

*p elimina referencia a la int**, lo que significa que intreprets el valor 7 int en la dirección X como un int*.

  • Si sizeof(int*) es mayor que sizeof(int), entonces la lectura como una int* leerán más allá del búfer asignado con new - comportamiento indefinido siempre independientemente de los tipos de datos y cualquier reintrepretation.

  • Si se trata del mismo tamaño, la CPU intenta leer la memoria que contiene el entero 7 como un int* - esto normalmente dará el valor int*(7), pero mirando a la Norma 5.2.10.5:

Un valor de tipo integral o tipo de enumeración se puede convertir explícitamente en un puntero. * [Nota: la conversión de una expresión constante integral (expr.const) con valor cero siempre produce un puntero nulo (conv.ptr), pero la conversión de otras expresiones que tengan valor cero no necesita dar un puntero nulo. --- end foonote] Un puntero convertido a un entero de tamaño suficiente (si existe alguno en la implementación) y de regreso al mismo tipo de puntero tendrá su valor original; las asignaciones entre los punteros y los enteros son, de lo contrario, definidas por la implementación.

Por lo tanto, se garantiza que el valor entero se convertirá en algún valor de puntero, nada indefinido, pero el valor está definido por la implementación. Aún así, al tratarse de una operación reversible, es que probablemente sea que el int 7 arrojará int* con el valor 7, lo que explica el comportamiento observado al generar "7".

  • Si el tamaño del puntero es inferior al del int, se leerá una porción del valor int e interpretar eso como un puntero. Dependiendo de la endianidad, ese segmento puede ser 0 o 7 o incluso algo más, pero de nuevo como int debe ser convertible a algún valor de puntero que se mostrará.
Cuestiones relacionadas