c - reales - libro de android studio en español pdf
¿Por qué la des-referencia NULL en este fragmento de C no causa un comportamiento indefinido? (1)
Encontré un fragmento de código donde NULL está encasillado en un tipo de puntero de estructura (foo *) 0
, y con ese puntero desreferenciando un miembro ((foo *)0)->m
, y usando la dirección de ese &(((foo *)0)->m))
y escribe fundiéndolo en enteros para obtener el índice de memoria de ese miembro en la estructura. ((unsigned int)(&(((foo *)0)->m)))
.
Que yo sepa, la desreferencia del puntero NULL siempre debería dar como resultado un error de segmentación en C. Pero no entiendo cómo el puntero NULL puede ser desreferenciado de esta manera y aún así no dar como resultado un error de segmentación.
#include <stdio.h>
#define MACRO(m) ((unsigned int)(&(((foo *)0)->m)))
typedef struct
{
int a;
int b;
int c;
int d;
}foo;
int main(void) {
printf("/n %d ", MACRO(c));
return 0;
}
El estándar C11 dice en 6.5.3.2 Operadores de dirección e indirección :
El operador unario arroja la dirección de su operando. Si el operando tiene el tipo '''' tipo '''', el resultado tiene el tipo '''' puntero para escribir ''''. Si el operando es el resultado de un operador unario *, ni ese operador ni el operador & se evalúan y el resultado es como si ambos se hubieran omitido, excepto que las restricciones sobre los operadores todavía se aplican y el resultado no es un valor l. De manera similar, si el operando es el resultado de un operador [], ni el operador & ni el unario * implícito en [] se evalúa y el resultado es como si el operador & se hubiera eliminado y el operador [] se hubiera cambiado a un operador + De lo contrario, el resultado es un puntero al objeto o función designado por su operando.
(Énfasis mío). Y la nota al pie dice:
Por lo tanto, & * E es equivalente a E (incluso si E es un puntero nulo)
Curiosamente, no hay excepciones para el operador ->
. No sé si esto se hizo deliberadamente o si se trata de un descuido. Entonces, en una interpretación estricta del estándar, diría que &(((foo *)0)->m)
es un comportamiento indefinido . Sin embargo, esto no significa que un programa tenga que bloquearse o que el compilador tenga que quejarse.
Dicho esto, es completamente razonable hacer la misma excepción al tomar la dirección del resultado de un operador ->
, y eso es lo que hacen la mayoría de los compiladores.