2010-07-13 47 views
6

Estoy intentando comprender cómo funcionan los punteros a los objetos asignados estáticamente y dónde pueden salir mal.Punteros a objetos asignados estáticamente

de escribir este código:

int* pinf = NULL; 
for (int i = 0; i<1;i++) { 
    int inf = 4; 
    pinf = &inf; 
} 

cout<<"inf"<< (*pinf)<<endl; 

Me sorprendió que funcionó robaba pensé que sería inf desaparecer cuando el programa salió del bloque y el puntero podría apuntar a algo que ya no existe. Esperaba una falla de segmentación cuando intentaba acceder al pinf. ¿En qué etapa del programa fallaría inf?

+6

No hay un objeto estáticamente asignado en su código. –

+2

... con la excepción de cout, por supuesto :-) –

Respuesta

7

Tu conocimiento es el correcto. inf desaparece cuando sale del alcance del ciclo, por lo que acceder al *pinf produce un comportamiento indefinido. Comportamiento indefinido significa que el compilador y/o el programa pueden hacer cualquier cosa, que puede ser un bloqueo, o en este caso puede ser simplemente avanzar.

Esto es porque inf está en la pila. Incluso cuando está fuera del alcance, pinf aún apunta a una ubicación de memoria utilizable en la pila. En lo que respecta al tiempo de ejecución, la dirección de la pila está bien, y el compilador no se molesta en insertar el código para verificar que no está accediendo a las ubicaciones más allá del final de la pila. Eso sería prohibitivamente caro en un lenguaje diseñado para la velocidad.

Por esta razón, debe tener mucho cuidado para evitar un comportamiento indefinido. C y C++ no son agradables como Java o C# donde las operaciones ilegales casi siempre generan una excepción inmediata y bloquean su programa. Usted, el programador, debe estar alerta porque el compilador se perderá todo tipo de errores elementales que cometa.

+0

+1 para la información (técnicamente correcta) :) –

+0

bueno, la dirección sigue siendo válida, por lo que seguramente no se bloqueará, y esto es aún peor. – ruslik

2

Probablemente nunca morirá porque PINF apuntará a algo en la pila.

Las pilas no suelen encogerse.

Modifíquelo y se le garantizará una sobreescritura.

1

No necesariamente obtendrá un SIGSEGV (fallo de segmentación). La memoria inf probablemente esté asignada en la pila. Y la región de memoria de la pila probablemente todavía esté asignada a su proceso en ese punto, entonces, esa es probablemente la razón por la cual no está obteniendo un fallo seg.

+0

Especialmente si ejecuta en Windows, donde obtendría una infracción de acceso en lugar de una falla de segmentación. – Puppy

+0

@DeadMG: jejejeje ... Es cierto. –

0

Se produce un error de protección cuando la página de memoria que señala ya no es válida para el proceso.

Afortunadamente, la mayoría de los sistemas operativos no crean una página separada para el espacio de pila de cada entero.

+0

O desafortunadamente, ya que permite que los errores continúen silenciosamente en su camino y aparezcan mucho más tarde con horribles consecuencias. Hay cerca eléctrica (efence) que forzará las asignaciones de pila en su propio bloque y ayudará a detectar estos errores, pero no creo que exista tal solución (o de hecho, podría serlo) para las variables de pila. –

1

El comportamiento no está definido, pero en la práctica, "destruir" un int es un noop, por lo que la mayoría de los compiladores dejarán el número solo en la pila hasta que ocurra algo más para reutilizar esa ranura en particular.

Algunos compiladores pueden establecer el int en 0xDEADBEEF (o algo así como basura) cuando sale del ámbito en modo de depuración, pero eso no hará que el cout << ... falle; simplemente imprimirá el valor sin sentido.

3

Se usa el llamado Dangling pointer. Se traducirá en un comportamiento indefinido según el estándar C++.

2

Si usted está planteando esto:

int main() { 
    int* pinf = NULL; 
    for (int i = 0; i<1;i++){ 
    int inf = 4; 
    pinf = &inf; 
    } 
    cout<<"inf"<< (*pinf)<<endl; 
} 

Entonces lo que tienes es un comportamiento indefinido. El objeto inf asignado automáticamente (no estático) se ha salido del alcance y originalmente se ha destruido al acceder a él a través del puntero. En este caso, podría pasar cualquier cosa, incluso que parezca que "funciona".

+0

+1 para escribir Comportamiento no definido. –

1

La memoria puede contener o no un 4 cuando llega a su línea de cout. Puede contener un 4 estrictamente por accidente. :)

Lo primero es lo primero: el sistema operativo solo puede detectar el acceso a la memoria extraviado en límites de página. Por lo tanto, si tiene 4k, 8k o 16k o más. (Verifique el /proc/self/maps en un sistema Linux algún día para ver el diseño de la memoria de un proceso; se permiten las direcciones en los rangos listados, no se permiten las que estén fuera de los rangos listados. Todos los sistemas operativos modernos en CPU con memoria protegida soportarán un mecanismo similar , así que será instructivo incluso si no estás interesado en Linux. Solo sé que es fácil en Linux.) Por lo tanto, el sistema operativo no puede ayudarte cuando tus datos son tan pequeños.

Además, su int inf = 4; podría muy bien ser escondido en las .rodata, .data o .text segmentos de su programa. Las variables estáticas pueden incluirse en cualquiera de estas secciones (no tengo idea de cómo lo decide el compilador/vinculador, lo considero mágico) y, por lo tanto, serán válidas durante toda la duración del programa. Comprueba size /bin/sh la próxima vez que estés en un sistema Unix para obtener una idea de la cantidad de datos que se colocan en cada sección. (Y echa un vistazo readelf(1) de demasiada información. objdump(1) si se encuentra en los sistemas más antiguos.)

Si cambia inf = 4 a inf = i, a continuación, el almacenamiento será asignado en la pila, y que tienen una mejor oportunidad de haciendo que se sobrescriba rápidamente.

Cuestiones relacionadas