Está desreferenciando el puntero nulo válido en tamaño de operación

Me encontré con un fragmento de código que, para mí, debería bloquearse 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 el 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 de la char * se resolverá con el tamaño del puntero en la architecture dada, pero ¿no debería colapsar y quemar mientras se desmarcaba un puntero NULL ?

¿Por qué funciona esto?

Esto funciona porque sizeof es una construcción en tiempo de comstackción, con la excepción de las matrices de longitud variable que no se evalúa en absoluto. Si miramos el borrador del estándar C99 sección 6.5.3.4 el operador de tamaño 2 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 tiempo de comstackción, se debe determinar 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 boostá x , que es bastante limpio.

Actualizar

Como señalo en mi respuesta a ¿Por qué sizeof (x ++) no incrementa x? hay una excepción al sizeof ser una operación de tiempo de comstackción y es cuando su operando es una matriz de longitud variable ( VLA ). Aunque no lo 6.5.3.4 anteriormente, la cita de 6.5.3.4 anterior dice esto.

Aunque en C11 en comparación con 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: ¿No evaluar la expresión a la que se aplica sizeof hace que sea legal desreferenciar un puntero nulo o no válido dentro de sizeof en C ++? .

El operador sizeof es una operación pura en tiempo de comstackción. Nada se hace en tiempo de ejecución, por lo que funciona bien.

Por cierto, el miembro de path no es realmente un puntero, por lo que técnicamente no puede ser NULL .

Afirmar que sizeof es una construcción puramente en tiempo de comstackción (como lo hacen las respuestas actuales) no es del todo exacto. Desde C99, sizeof no es una construcción de tiempo puramente de comstackción. El operando de sizeof se evalúa en tiempo de ejecución del tipo de operando es un VLA. Las respuestas publicadas hasta ahora parecen ignorar esa posibilidad.

Tu 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 se debe evaluar el argumento de sizeof (es decir, se supone que debo boost, consulte https://ideone.com/9Fv6xC ). Sin embargo, no estoy del todo seguro de que la desreferencia de punto nulo en a[0] se suponga que produzca un comportamiento indefinido aquí.