2010-06-15 27 views
5

Citando de la sección C-std 6.7.2.1,miembro de la matriz flexible en C-estructura

struct s { int n; double d[]; }; 

Esta es una declaración de estructura válida. Estoy buscando algún uso práctico de este tipo de sintaxis. Para ser precisos, ¿cómo es esta construcción más o menos poderosa que mantener un doble * como el segundo elemento? ¿O es este otro caso de 'usted-puede-hacerlo-en-múltiples-formas'?

Arpan

+2

Ah, es un buen ejemplo que demuestra una vez más que las matrices y punteros no son en absoluto iguales :) – fredoverflow

Respuesta

10

Las respuestas C FAQ precisamente esta pregunta. La respuesta rápida es que esta estructura incluirá la matriz double dentro de la estructura en lugar de un puntero a una matriz fuera de la estructura. Como un ejemplo rápido, podría utilizar su estructura como en este ejemplo:

struct s mystruct = malloc(sizeof(struct s) + 5 * sizeof(double)); 
s.n = 12; 
s.d[0] = 4.0; 
s.d[1] = 5.0; 
s.d[2] = 6.0; 
s.d[3] = 7.0; 
s.d[4] = 8.0; 

Y así sucesivamente - el tamaño de la matriz que quiere está incluido en la asignación, y luego se puede usar como cualquier variedad . Normalmente, dicho tipo contiene el tamaño como parte de la estructura, ya que el uso del truco + para omitir una matriz del tipo s será necesariamente complicado por esta situación.

A su pregunta añadida '¿cómo se construyen más o menos potente que mantener un [puntero] como el segundo elemento?', No es más poderosa per se, pero no es necesario para mantener un puntero alrededor, por lo que ahorraría al menos ese espacio; también cuando está copiando la estructura, también copiará la matriz, en lugar de un puntero a una matriz, una diferencia sutil a veces, pero muy importante otras veces. 'You-can-do-it-in-multiple-ways' es probablemente una buena explicación, pero hay casos en los que desea específicamente un diseño u otro.

+0

así struct s s1 = malloc (...); y luego struct s s2 = s1; significaría que s2 obtiene una matriz que se crea automágicamente y se copia el contenido de s1?lo mismo ocurre si en lugar de los tipos de POD struct s tiene una clase definida por el usuario como segundo elemento? – Fanatic23

+0

No, no habría copia mágica con la asignación de estructura; pero si usa 'memcpy()' con el tamaño apropiado, funcionará. Si tiene un puntero, deberá asignar memoria y copiar el conjunto por separado. –

+1

No estoy seguro de que ese enlace a la C FAQ, q2.6, realmente responda esta pregunta. Si lo hace, es solo en un sentido arcano que solo tendría sentido para alguien que ya sabe la respuesta. De hecho, el enlace sugiere que, si se trata de lo mismo, no se debe considerar portátil. – BobbyShaftoe

0

He visto esto usado en Windows para cadenas que están etiquetadas por su longitud. Los datos del personaje se almacenan directamente después de la longitud en la memoria, manteniendo todo ordenado.

typedef struct { 
    SIZE_T bytes; 
    TCHAR chars[]; 
} tagged_string; 
1

Se puede utilizar para agregar los campos de cabecera a las matrices, el más común de lo que sería su tamaño asignado dinámicamente:

struct int_array 
{ 
    size_t size; 
    int values[]; 
}; 

struct int_array *foo = malloc(sizeof *foo + 42 * sizeof *foo->values); 
foo->size = 42; 

... 

for(size_t i = 0; i < foo->size; ++i) 
    foo->values[i] = i * i; 

Se podría lograr resultados similares mediante el uso de un miembro de int * lugar y asignar la matriz por separado, pero sería menos eficiente tanto en términos de memoria (puntero adicional, gestión de pila para el segundo bloque de memoria) como en tiempo de ejecución (indirección adicional, segunda asignación).

4

La principal ventaja es que un miembro de matriz flexible le permite asignar un solo bloque de memoria para la matriz junto con los otros datos en la estructura (con un puntero, normalmente terminaría con dos asignados por separado bloques).

También es útil con datos transmitidos por bastantes protocolos de red, donde el flujo entrante se define de la misma manera: un entero que define una longitud, seguido de muchas unidades (normalmente bytes/octetos) de datos. Puede (típicamente) usar un tipo de juego de palabras para superponer una estructura con un miembro de matriz flexible en un búfer lleno de dichos datos, y trabajar con él directamente en lugar de tener que analizarlo en pedazos y luego trabajar con las piezas individualmente.

+0

En mi experiencia, la implementación de un protocolo de red (o un formato de archivo, que es esencialmente el mismo problema) al escribir un búfer de bytes en un tipo de estructura suele ser un caso de You're Doing It Wrong. Deserializarlo campo por campo en cambio termina siendo mucho más portátil. – caf

+0

@caf: Deserializar campo por campo es más portátil, pero el tipo de juego de palabras puede permitir en algunos casos que el código sea más legible y más eficiente, especialmente si puede construir una tabla de punteros a cosas almacenadas dentro de un búfer existente, en lugar de tener que asigna espacio para una segunda copia de toda la información y luego copia toda la información del búfer de bytes en el espacio recién asignado. Lo que hubiera hecho las cosas realmente portátiles habría sido si C hubiera soportado las estructuras de "diseño explícito", por lo que el código podría decir, p. "Necesito un tipo de datos de 64 bytes, puede ubicarse ... – supercat

+0

... en cualquier límite de 2 bytes e incluye [entre otras cosas] un entero de 32 bits llamado" Woozle "almacenado en el desplazamiento 12 como cuatro octetos en el orden little-endian ". Tener un compilador que soporte ese tipo de cosas y manejarlo eficientemente en casos donde coincida con el diseño natural de un compilador sería más barato que tratar de reconocer y optimizar todas las diferentes variaciones en '(((uint32_t) ptr [15] << 24) | ((uint32_t) ptr [14] << 16) | ((uint32_t) ptr [13] << 8) | ptr [12]) 'que podría reemplazarse con un par de cargas de 16 bits de la dirección ptr + 12 y ptr + 14, o una única carga de 32 bits desde ptr + 12. – supercat

Cuestiones relacionadas