operaciones - punteros y arreglos en c
¿Es válida la desreferenciación del puntero nulo en la operación sizeof (3)
Esta pregunta ya tiene una respuesta aquí:
Me he topado con un fragmento de código que para mí debería fallar con una falla de segmentación y, sin embargo, funciona sin problemas. El código en cuestión más la estructura de datos relevante es la siguiente (con el comentario asociado que se encuentra arriba):
typedef struct {
double length;
unsigned char nPlaced;
unsigned char path[0];
}
RouteDefinition* Alloc_RouteDefinition()
{
// NB: The +nBags*sizeof.. trick "expands" the path[0] array in RouteDefinition
// to the path[nBags] array
RouteDefinition *def = NULL;
return (RouteDefinition*) malloc(sizeof(RouteDefinition) + nBags * sizeof(def->path[0]));
}
¿Por qué funciona esto? Supongo que el tamaño del carácter * se resolverá con el tamaño del puntero en la arquitectura dada, pero ¿no debería bloquearse y quemarse al eliminar la referencia a un punto NULL
?
¿Por qué funciona esto?
Esto funciona porque sizeof es una construcción de tiempo de compilación, con la excepción de que las matrices de longitud variable no se evalúan en absoluto. Si nos fijamos en el borrador de la sección C99, sección 6.5.3.4
el tamaño del párrafo 2 del operador dice ( énfasis mío ):
[...] El tamaño se determina a partir del tipo de operando. El resultado es un entero. Si el tipo del operando es un tipo de matriz de longitud variable, se evalúa el operando; de lo contrario, el operando no se evalúa y el resultado es una constante entera.
También vemos el siguiente ejemplo en el párrafo 5 que confirma esto:
double *dp = alloc(sizeof *dp);
^^^ ^
|
This is not the use of uninitialized pointer
En el momento de la compilación, se determina el tipo de expresión con el fin de calcular el resultado. Podemos demostrar esto con el siguiente ejemplo:
int x = 0 ;
printf("%zu/n", sizeof( x++ ));
que no incrementará x
, que es bastante limpio.
Actualizar
Como observo en mi respuesta a ¿Por qué sizeof (x ++) no incrementa x? Hay una excepción a que sizeof
es una operación de tiempo de compilación y es cuando su operando es una matriz de longitud variable ( VLA ). Aunque no lo 6.5.3.4
anteriormente, la cita del punto 6.5.3.4
anterior sí lo dice.
Aunque en C11 a diferencia de C99, no se especifica si sizeof
se evalúa o no en este caso.
Además, tenga en cuenta que hay una versión en C ++ de esta pregunta: ¿la evaluación de la expresión a la que se aplica sizeof hace que sea legal eliminar la referencia de un puntero nulo o no válido dentro de sizeof en C ++? .
Afirmar que sizeof
es una construcción puramente de tiempo de compilación (como hacen las respuestas actuales) no es del todo exacto. Desde C99, sizeof
no es una construcción de tiempo puramente compilación. El operando de sizeof
se evalúa en el tiempo de ejecución del tipo de operando que es un VLA. Las respuestas publicadas hasta ahora parecen ignorar esa posibilidad.
Su código está bien, ya que no implica ningún VLA. Sin embargo, algo como esto puede ser una historia diferente.
unsigned n = 10;
int (*a)[n] = NULL; // `a` is a pointer to a VLA
unsigned i = 0;
sizeof a[i++]; // applying `sizeof` to a VLA
De acuerdo con el estándar C99, se supone que el argumento de sizeof
debe ser evaluado (es decir, se supone que debo incrementar, consulte https://ideone.com/9Fv6xC ). Sin embargo, no estoy completamente seguro de que la desreferencia de punto nulo en a[0]
deba producir un comportamiento indefinido aquí.
El operador sizeof
es una operación de compilación pura. No se hace nada en tiempo de ejecución, por lo que funciona bien.
Por cierto, el miembro de la path
no es realmente un puntero, por lo que técnicamente no puede ser NULL
.