Pensé que realmente entendía esto, y volver a leer el estándar (ISO 9899: 1990) simplemente confirma mi comprensión obviamente errónea, así que ahora pregunto aquí.Puntero vs matriz en C, diferencia no trivial
Los siguientes errores en el programa:
#include <stdio.h>
#include <stddef.h>
typedef struct {
int array[3];
} type1_t;
typedef struct {
int *ptr;
} type2_t;
type1_t my_test = { {1, 2, 3} };
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
type1_t *type1_p = &my_test;
type2_t *type2_p = (type2_t *) &my_test;
printf("offsetof(type1_t, array) = %lu\n", offsetof(type1_t, array)); // 0
printf("my_test.array[0] = %d\n", my_test.array[0]);
printf("type1_p->array[0] = %d\n", type1_p->array[0]);
printf("type2_p->ptr[0] = %d\n", type2_p->ptr[0]); // this line crashes
return 0;
}
Comparando las expresiones my_test.array[0]
type2_p->ptr[0]
y de acuerdo con mi interpretación de la norma:
6.3.2.1 subíndices de matriz
"La definición de la El operador de subíndice [] es que E1 [E2] es idéntico a (* ((E1) + (E2))). "
Aplicando esto da:
my_test.array[0]
(*((E1)+(E2)))
(*((my_test.array)+(0)))
(*(my_test.array+0))
(*(my_test.array))
(*my_test.array)
*my_test.array
type2_p->ptr[0]
*((E1)+(E2)))
(*((type2_p->ptr)+(0)))
(*(type2_p->ptr+0))
(*(type2_p->ptr))
(*type2_p->ptr)
*type2_p->ptr
type2_p->ptr
tiene tipo "puntero a int" y el valor es la dirección de inicio de my_test
. *type2_p->ptr
por lo tanto evalúa a un objeto entero cuyo almacenamiento está en la misma dirección que my_test
tiene.
adicional:
6.2.2.1 Lvalues, matrices y designadores de función
"A menos que cuando es el operando del operador sizeof o el unario & operador, ..., An lvalue que tiene tipo
array of type
se convierte en una expresión con tipopointer to type
que apunta al elemento inicial del objeto de matriz y no es y lvalue. "
my_test.array
tiene tipo "matriz de int" y es lo descrito antes convertido a "puntero a int" con la dirección del primer elemento como el valor. *my_test.array
por lo tanto, se evalúa como un objeto entero cuyo almacenamiento está en la misma dirección que el primer elemento en la matriz.
Y finalmente
6.5.2.1 Estructura y especificadores de unión
Un puntero a un objeto de estructura, convenientemente convertido, apunta a su miembro inicial ..., y viceversa. Puede haber un relleno sin nombre dentro de un objeto de estructura , pero no en su inicio , según sea necesario para lograr la alineación apropiada de .
Desde el primer miembro de type1_t
es la matriz, la dirección de inicio de que y todo el type1_t
objeto es el mismo que el descrito anteriormente. Entendí, por lo tanto, que *type2_p->ptr
evalúa a un entero cuyo almacenamiento está en la misma dirección que el primer elemento en la matriz y por lo tanto es idéntico a *my_test.array
.
Pero esto no puede ser el caso, debido a que el programa se bloquea constantemente en Solaris, Linux y cygwin con versiones de gcc 2.95.3, 3.4.4 y 4.3.2 , por lo que cualquier problema ambiental está completamente fuera de la cuestión.
¿Dónde está mi razonamiento errado/qué no entiendo? ¿Cómo declaro type2_t para hacer ptr point al primer miembro de la matriz?
Definitivamente es un comportamiento definido. La dirección de ptr es la misma que la dirección de my_array. my_array es en realidad un puntero a la estructura, mientras que ptr es simplemente un puntero entero dentro de una estructura. – Vitali
"comportamiento definido" no significa "algo sucede", significa que "lo que sucede está definido por el estándar". El tipo de juego de palabras es un comportamiento indefinido. Si quieres ver que algo sorprendente sucede cuando escribes juego de palabras, aumenta las optimizaciones una o dos muescas en tu compilador. –