¿Cuáles son algunos buenos usos del miembro de matriz flexible de una estructura introducida por el estándar C99?

Lo uso todo el tiempo cuando trato con paquetes de red. Un paquete es un conjunto de encabezados de varios protocolos. Es posible que tenga algo como esto:
encabezado ethernet: 14 bytes
Encabezado IP: 20 bytes
Encabezado TCP: 20 bytes
datos

Puedo definir una estructura usando esto:

struct {
struct ether_header ethh;
struct ip iph;
struct tcphdr tcph;
unsigned char data[];
} pkt;

Ahora, cuando entra un paquete en la escritura, la longitud del paquete está en el encabezado IP. Esto es dinámico, por supuesto, ya que todos los paquetes no son del mismo tamaño. Puedo hacer esto:

uchar *packet; // The packet from the wire with EH, IP
struct pkt *packetWithData;
int len=*((struct pkt *)packet)->iph.ip_len * sizeof(uchar) + sizeof(ether_header);
packetWithData = (struct pkt *)malloc(len);
memcpy(packetWithData,packet,len);

Ahora tengo espacio para todos los encabezados, así como para los datos, y puedo acceder a todos los campos de encabezado, así como a los campos de datos, utilizando una sola estructura con un solo puntero. Se libera en una sola operación. Si lo hice:

struct {
struct ether_header ethh;
struct ip iph;
struct tcphdr tcph;
unsigned char *data;
} pkt;

Necesitaría 8 bytes adicionales de memoria para el puntero a los datos por paquete. Copiarlo requiere dos operaciones: copiar encabezados, copiar datos. Dos libres, etc.

También lo uso para mantener cadenas de aminoácidos en proteínas.

struct {
uint64 *id;
uint64 aacids;
...
struct aacid acids[];
}

Ahora, a medida que construyo proteínas de longitud desconocida en alguna reacción biológica molecular, puedo reasignar y copiar la porción de ácidos y, cuando lo hago, tengo una sola estructura con la cadena de ácido que puedo pasar en el programa en lugar de muchos punteros a estructuras asignadas. Hago esto todo el tiempo con grandes datos donde se requiere un encabezado.

Debe usar este tipo de estructura en los motores de base de datos para cosas como filas y estructuras de página. Las filas tienen una estructura desconocida en tiempo de ejecución, ya que la estructura se define mediante la instrucción CREATE TABLE de un usuario y se guarda en metadatos. Algo como este tipo de declaración se ha utilizado en C durante mucho tiempo, aunque la cosa de longitud variable era un carácter sin signo [1].

Una estructura de fila común se parece a algo

typedef struct Row {
RowHeader hdr;
bytes de caracteres sin firmar [1];
} Fila;

El contenido de los bytes incluye dos estructuras de longitud variable: una matriz de compensaciones a los valores reales de la columna y los valores de la columna en sí. Una arruga es que la matriz de desplazamientos será en sí misma diferente para las filas de diferentes tamaños: si la longitud total del área de datos de una fila es inferior a 256 bytes, la matriz de desplazamiento será una matriz de caracteres sin signo, y si es mayor que esto, será una serie de cortos sin firmar.

Los valores de columna también pueden ser estructuras de longitud variable, particularmente cadenas utilizadas para CHAR y VARCHAR (la mayoría de los esquemas de almacenamiento de db implementan CHAR y VARCHAR usando el mismo formato, con el relleno de espacio CHAR por encima del nivel de almacenamiento) y algunos tipos de NÚMERO de longitud variable / Estructuras NUMÉRICAS.

Intentar usar punteros para todo esto simplemente no funcionará, ya que las Filas mismas deben ser contiguas en la memoria ya que viven en estructuras de Página, que tienen una disposición similar de longitud variable:

typedef struct Página {
PageHeader hdr;
bytes de caracteres sin firmar [1];
} Página

Una página será una colección de filas, con una buena cantidad de metadatos necesarios para indicar dónde se debe escribir la siguiente fila, cuánto espacio libre existe, una serie de compensaciones de fila y bits “usados ​​/ libres” (a menudo “hacia atrás” comenzando en la parte inferior de la página), y varios otros metastuff, todos escritos en el área “bytes”.

La página en sí tendrá un tamaño fijo, por lo que bien puede declararse con un tamaño para bytes, pero el contenido de los bytes será una estructura dinámica muy compleja que no es posible definir en tiempo de compilación.

Esto se usó en todo el lugar mucho antes de la estandarización en C99, incluso en las API POSIX y Windows (por ejemplo, struct _TOKEN_GROUPS), excepto que el tamaño de la matriz tenía que ser 1 para la portabilidad.

Aprovechando mi linux / usr / include ahora veo struct audit_sig_info, struct _ftsent, struct _nc_eventlist, struct gcry_md_handle y muchos más usos de esta función (principalmente en su encarnación anterior a C99)