La diferencia del puntero y una matriz.

Tengo un trozo de código abajo, ¿y cuál es la diferencia de ellos? El primero, la dirección del elemento buf de la estructura es 4 más grande que la de la estructura, mientras que el segundo no lo es.

primero

 #include  typedef struct A { int i; char buf[]; //Here }A; int main() { A *pa = malloc(sizeof(A)); char *p = malloc(13); memcpy(p, "helloworld", 10); memcpy(pa->buf, p, 13); printf("%x %x %d %s\n", pa->buf, pa, (char *)pa->buf - (char *)pa, pa->buf); } 

Segundo

 typedef struct A { int i; char *buf; //Here }A; 

El primero es un ‘miembro de matriz flexible’ de C99. El segundo es el respaldo confiable para cuando no tiene C99 o posterior.

Con un miembro de matriz flexible, asigna el espacio que necesita para la matriz junto con la estructura principal:

 A *pa = malloc(sizeof(A) + strlen(string) + 1); pa->i = index; strcpy(pa->buf, string); ...use pa... free(pa); 

En cuanto a la asignación de memoria, el miembro buf no tiene tamaño (por lo que sizeof(A) == sizeof(int) menos que haya problemas de relleno debido a la alineación de la matriz, por ejemplo, si tiene una matriz flexible de double ).

La alternativa requiere dos asignaciones (y dos versiones), o algún cuidado en la configuración:

 typedef struct A2 { int i; char *buf; } A2; A2 *pa2 = malloc(sizeof(A2)); pa2->buff = strdup(string); ...use pa2... free(pa2->buff); free(pa2); 

O:

 A2 *pa2 = malloc(sizeof(A2) + strlen(string) + 1); pa2->buff = (char *)pa2 + sizeof(A2); ...use pa2... free(pa2); 

Tenga en cuenta que el uso de A2 requiere más memoria, ya sea por el tamaño del puntero (asignación única) o por el tamaño del puntero y la sobrecarga de la segunda asignación de memoria (asignación doble).

A veces verás algo conocido como ‘struct hack’ en uso; esto es anterior al estándar C99 y está obsoleto por los miembros de la matriz flexible. El código para esto se ve así:

 typedef struct A3 { int i; char buf[1]; } A3; A3 *pa3 = malloc(sizeof(A3) + strlen(string) + 1); strcpy(pa3->buf, string); 

Esto es casi lo mismo que un miembro de matriz flexible, pero la estructura es más grande. En el ejemplo, en la mayoría de las máquinas, la estructura A3 tendría una longitud de 8 bytes (en lugar de 4 bytes para A ).

GCC tiene algún soporte para arreglos de longitud cero; es posible que vea la estructura hack con una dimensión de matriz de 0. Eso no es portátil para ningún comstackdor que no imite a GCC.

Se llama “estructura hack” porque no está garantizado que sea portátil según el estándar de idioma (porque está accediendo fuera de los límites de la matriz declarada). Sin embargo, empíricamente, ‘siempre ha funcionado’ y probablemente continuará haciéndolo. Sin embargo, debe utilizar miembros de matriz flexible en lugar de la estructura hackear.


ISO / IEC 9899: 2011 §6.7.2.1 Especificadores de estructura y unión

¶3 Una estructura o unión no debe contener un miembro con un tipo de función o incompleto (por lo tanto, una estructura no debe contener una instancia de sí misma, pero puede contener un puntero a una instancia de sí misma), excepto que el último miembro de una estructura con más de un miembro nombrado puede tener un tipo de matriz incompleta; dicha estructura (y cualquier unión que contenga, posiblemente recursivamente, un miembro que sea una estructura de este tipo) no debe ser miembro de una estructura o elemento de una matriz.

¶18 Como caso especial, el último elemento de una estructura con más de un miembro nombrado puede tener un tipo de matriz incompleta; Esto se llama un miembro de matriz flexible . En la mayoría de las situaciones, el miembro de la matriz flexible se ignora. En particular, el tamaño de la estructura es como si se hubiera omitido el miembro de la matriz flexible, excepto que puede tener más relleno posterior del que implicaría la omisión. Sin embargo, cuando un . (o -> ) el operador tiene un operando izquierdo que es (un puntero a) una estructura con un miembro de matriz flexible y el operando derecho nombra a ese miembro, se comporta como si ese miembro hubiera sido reemplazado por la matriz más larga (con el mismo tipo de elemento ) que no haría la estructura más grande que el objeto al que se accede; el desplazamiento de la matriz seguirá siendo el del miembro de la matriz flexible, incluso si fuera diferente de la matriz de reemplazo. Si esta matriz no tuviera elementos, se comporta como si tuviera un elemento, pero el comportamiento no está definido si se intenta acceder a ese elemento o generar un puntero que lo supera.

 struct A { int i; char buf[]; }; 

no reserva ningún espacio para la matriz, o para un puntero a una matriz. Lo que esto dice es que una matriz puede seguir directamente el cuerpo de A y se puede acceder a través de buf , así:

 struct A *a = malloc(sizeof(*a) + 6); strcpy(a->buf, "hello"); assert(a->buf[0] == 'h'); assert(a->buf[5] == '\0'; 

Tenga en cuenta que reservé 6 bytes después de “hola” y el terminador nulo.

La forma del puntero utiliza un direccionamiento indirecto (la memoria podría ser contigua, pero esto no depende ni se requiere)

 struct B { int i; char *buf; }; /* requiring two allocations: */ struct B *b1 = malloc(sizeof(*b1)); b1->buf = strdup("hello"); /* or some pointer arithmetic */ struct B *b2 = malloc(sizeof(*b2) + 6); b2->buf = (char *)((&b2->buf)+1); 

El segundo ahora se presenta igual que el anterior, excepto con un puntero entre el entero y la matriz de caracteres.