¿Son los miembros flexibles de la matriz realmente necesarios?
struct flexible-array-member (2)
Esos conceptos definitivamente no son necesarios como usted mismo ha señalado.
Las diferencias entre los dos que ha demostrado son dónde están ubicados sus datos en la memoria.
En el primer ejemplo con matriz flexible, sus metadatos y la matriz están en el mismo bloque de memoria y pueden moverse como un bloque (puntero) si es necesario.
En el segundo ejemplo, sus metadatos están en la pila y su matriz está en otro lugar en el montón. Para moverlo / copiarlo, ahora necesitará mover dos bloques de memoria y actualizar el puntero en su estructura de metadatos.
En general, las matrices de tamaños flexibles se utilizan cuando necesita colocar una matriz y sus metadatos están espacialmente juntos en la memoria.
Un ejemplo en el que esto es definitivamente útil es, por ejemplo, al colocar una matriz con sus metadatos en un archivo: solo tiene un bloque continuo de memoria y cada vez que lo cargue, (lo más probable) se colocará en una ubicación diferente de su VM .
Una estructura con un miembro de matriz flexible, aparentemente, no está destinada a ser declarada, sino que se usa junto con un puntero a esa estructura. Al declarar un miembro de matriz flexible, debe haber al menos otro miembro, y el miembro de matriz flexible debe ser el último miembro en esa estructura.
Digamos que tengo uno que se ve así:
struct example{
int n;
int flm[];
}
Luego para usarlo, tendré que declarar un puntero y usar malloc para reservar memoria para el contenido de la estructura.
struct example *ptr = malloc(sizeof(struct example) + 5*sizeof(int));
Es decir, si quiero que mi matriz flm [] contenga cinco enteros. Entonces, puedo usar mi estructura así:
ptr->flm[0] = 1;
Mi pregunta es, ¿no debería ser capaz de simplemente usar un puntero en lugar de esto? No solo sería compatible antes de C99, sino que podría usarlo con o sin un puntero a esa estructura. Teniendo en cuenta que ya tengo que usar malloc con la película, ¿no debería ser capaz de hacer esto?
Considere esta nueva definición de la estructura de ejemplo;
struct example{
int n;
int *notflm;
}
struct example test = {4, malloc(sizeof(int) * 5)};
Incluso podría usar el reemplazo del mismo modo que el miembro flexible de la matriz:
¿Esto también funcionaría? (Siempre que la definición anterior de ejemplo con notflm)
struct example test;
test.n = 4;
notflm = malloc(sizeof(int) * 5);
Los punteros no son matrices. Las razones básicas para elegir cuál usar son las mismas que siempre son con matrices versus punteros. En el caso especial de los miembros de matriz flexible, aquí hay algunas razones por las que puede preferirlas a un puntero:
Reduciendo los requisitos de almacenamiento. Un puntero ampliará su estructura en (típicamente) 4 u 8 bytes, y gastará mucho más en gastos generales si asigna el almacenamiento apuntado por separado en lugar de con una sola llamada a
malloc
.Mejorando la eficiencia de acceso. Un miembro de matriz flexible está ubicado en un desplazamiento constante desde la base de la estructura. Un puntero requiere una desreferencia por separado. Esto afecta tanto a la cantidad de instrucciones necesarias para acceder a él como a la presión de registro.
Atomicidad del éxito / fracaso de la asignación. Si asigna la estructura y asigna el almacenamiento para que señale como dos pasos separados, su código para limpiar en los casos de falla será mucho más feo, ya que tiene el caso donde uno tuvo éxito y el otro falló. Esto se puede evitar con alguna aritmética de puntero para tallar fuera de la misma solicitud de
malloc
, pero es fácil confundir la lógica e invocar UB debido a problemas de alineación.Evitando la necesidad de una copia en profundidad. Si utiliza una matriz flexible en lugar de un puntero, puede simplemente memcpy (no asignar, ya que la asignación no puede conocer la longitud flexible de la matriz) para copiar la estructura en lugar de tener que copiar también los datos apuntados y corregir el puntero en la nueva copia.
Evitando la necesidad de free-free. Es muy conveniente y limpio poder
free
un solo objeto en lugar de tener quefree
datos apuntados también. Esto también se puede lograr con el enfoque "malloc
un solomalloc
" mencionado anteriormente, por supuesto, pero los arreglos flexibles lo hacen más fácil y menos propenso a errores.Sin duda muchas más razones ...