2011-01-06 10 views
5

Me sorprendió cuando el siguiente programa no se bloqueó.Acceso a los elementos de la estructura con punteros

typedef struct _x { 
    int a; 
    char b; 
    int c; 
} x; 

main() { 
    x *ptr = 0; 
    char *d = &ptr->b; 
} 

Según mi entendimiento que el operador -> tiene mayor prioridad sobre & operador. Así que esperaba que el programa fallara en la declaración siguiente cuando intentemos eliminar la referencia del puntero NULL tr.

char *d = &ptr->b; 

Pero la afirmación &ptr->b evalúa a una dirección válida. ¿Podría alguien explicar por favor dónde estoy equivocado?

+0

Esto es de alguna manera similar a la macro 'offsetof'. – ruslik

Respuesta

2

&ptr->b == sizeof(int), eso significa que el desplazamiento de b dentro _x después de _x.a (que es de tipo int) con relación a la dirección de *((x*)0). El desplazamiento de 4 (típico para la arquitectura de 32 bits) se guarda dentro del puntero d. Tienes que acceder al d para obtener un seg-fault.

4

El motivo por el que el código no se bloquea es porque en realidad no eliminó la referencia del puntero. Tenga en cuenta que la expresión

&ptr->b 

en realidad no intente cargar el contenido de ptr o ptr->b. En cambio, simplemente almacena la dirección de donde se encuentra en la memoria. Lo que terminará obteniendo es un puntero hacia donde debería estar el campo b del objeto señalado por ptr. Esto será unos pocos bytes después de la dirección 0, por lo que desreferenciar el puntero que acaba de crear provocará una segfault.

2

Calcular una dirección no requiere acceder a la memoria. &ptr->b significa "darme la dirección del campo b de la estructura apuntada por ptr". Hacerlo no requiere mirar lo que pueda estar almacenado en esa ubicación de memoria.

Puede ser útil pensar en indexar una matriz en lugar de una estructura. C define ptr[5] como equivalente a *(ptr + 5), lo que significa que &(ptr[5]) es lo mismo que &(*(ptr + 5)). Ahora es fácil ver que & y * "cancelan" y te dejan con (ptr + 5), lo que implica solo un incremento de puntero y no una carga de la memoria.

C lo hace ligeramente turbio porque distingue lvalues ​​de rvalues. Es decir, una expresión que se refiere a la memoria se trata de manera diferente en el lado izquierdo de una expresión que a la derecha. Dada una declaración como x = y;, un compilador de C cargará un valor desde la dirección de y, y lo almacenará en la dirección de x. Esta es la distinción: y se desreferencia implícitamente, pero x no lo es.

5

Sus expectativas eran infundadas. Los programas C no necesariamente "bloquean" cuando desreferencia punteros nulos. Los programas C exhiben el llamado comportamiento indefinido cuando intenta hacer algo como eso. El comportamiento indefinido puede manifestarse de muchas maneras diferentes. Puede provocar un bloqueo. O puede producir algo que incluso se asemeje a un programa "de trabajo". Lo último es lo que aparentemente sucedió en tu caso.

Pero en cualquier caso, el comportamiento de su programa no está definido.Y no, no produce una "dirección válida", como parece creer erróneamente. Una dirección numérica que corresponde a una ubicación en la memoria donde no existe ningún objeto no es válida (a excepción del valor nulo del puntero, por supuesto).

Cuestiones relacionadas