En el código del kernel de Linux encontré lo siguiente que no puedo entender.
struct bts_action {
u16 type;
u16 size;
u8 data[0];
} __attribute__ ((packed));
El código está aquí: http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h
¿Cuál es la necesidad y el propósito de una matriz de datos con cero elementos?
c
structure
flexible-array-member
Jeegar Patel
fuente
fuente
Respuestas:
Esta es una forma de tener tamaños variables de datos, sin tener que llamar
malloc
(kmalloc
en este caso) dos veces. Lo usarías así:Esto solía no ser estándar y se consideraba un truco (como dijo Aniket), pero estaba estandarizado en C99 . El formato estándar para él ahora es:
Tenga en cuenta que no menciona ningún tamaño para el
data
campo. Tenga en cuenta también que esta variable especial solo puede aparecer al final de la estructura.En C99, este asunto se explica en 6.7.2.1.16 (énfasis mío):
O en otras palabras, si tiene:
Puedes acceder
var->data
con índices en formato[0, extra)
. Tenga en cuenta quesizeof(struct something)
solo dará el tamaño teniendo en cuenta las otras variables, es decir, dadata
un tamaño de 0.También puede ser interesante observar cómo el estándar realmente da ejemplos de
malloc
tal construcción (6.7.2.1.17):Otra nota interesante por el estándar en la misma ubicación es (énfasis mío):
fuente
[0, extra)
?Este es un truco en realidad, de hecho para GCC ( C90 ).
También se llama truco de estructura .
Entonces, la próxima vez, diría:
Sería equivalente a decir:
Y puedo crear cualquier número de tales objetos de estructura.
fuente
La idea es permitir una matriz de tamaño variable al final de la estructura. Presumiblemente,
bts_action
es un paquete de datos con un encabezado de tamaño fijo (los campostype
ysize
) y undata
miembro de tamaño variable . Al declararlo como una matriz de longitud 0, se puede indexar como cualquier otra matriz. Luego, asignaría unabts_action
estructura, por ejemplo, de 1024 bytes dedata
tamaño, así:Véase también: http://c2.com/cgi/wiki?StructHack
fuente
malloc
te hace repetir varias veces y si alguna vez el tipo deaction
cambios, tienes que arreglarlo varias veces. Compare los dos siguientes por sí mismo y lo sabrá:struct some_thing *variable = (struct some_thing *)malloc(10 * sizeof(struct some_thing));
vs.struct some_thing *variable = malloc(10 * sizeof(*variable));
El segundo es más corto, más limpio y claramente más fácil de cambiar.El código no es válido C ( ver esto ). El kernel de Linux, por razones obvias, no se preocupa en lo más mínimo por la portabilidad, por lo que utiliza mucho código no estándar.
Lo que están haciendo es una extensión no estándar de GCC con un tamaño de matriz 0. Un programa compatible con el estándar se habría escrito
u8 data[];
y habría significado exactamente lo mismo. A los autores del kernel de Linux aparentemente les encanta hacer las cosas innecesariamente complicadas y no estándar, si se revela una opción para hacerlo.En los estándares C más antiguos, terminar una estructura con una matriz vacía se conocía como "el truco de estructura". Otros ya han explicado su propósito en otras respuestas. El hack de struct, en el estándar C90, era un comportamiento indefinido y podría causar bloqueos, principalmente porque un compilador de C es libre de agregar cualquier número de bytes de relleno al final de la estructura. Estos bytes de relleno pueden colisionar con los datos que intentó "piratear" al final de la estructura.
GCC al principio hizo una extensión no estándar para cambiar este comportamiento de indefinido a bien definido. Luego, el estándar C99 adaptó este concepto y, por lo tanto, cualquier programa C moderno puede utilizar esta función sin riesgo. Se conoce como miembro de matriz flexible en C99 / C11.
fuente
Otro uso de la matriz de longitud cero es como una etiqueta con nombre dentro de una estructura para ayudar a compilar la verificación de compensación de la estructura en el tiempo.
Suponga que tiene algunas definiciones de estructuras grandes (abarcan varias líneas de caché) que desea asegurarse de que estén alineadas con el límite de la línea de caché tanto al principio como en el medio donde cruza el límite.
En el código, puede declararlos usando extensiones GCC como:
Pero aún desea asegurarse de que esto se aplique en tiempo de ejecución.
Esto funcionaría para una sola estructura, pero sería difícil cubrir muchas estructuras, cada una tiene un nombre de miembro diferente para alinear. Lo más probable es que obtenga un código como el siguiente, donde tiene que encontrar los nombres del primer miembro de cada estructura:
En lugar de ir de esta manera, puede declarar una matriz de longitud cero en la estructura que actúa como una etiqueta con nombre con un nombre coherente pero no consume ningún espacio.
Entonces el código de aserción en tiempo de ejecución sería mucho más fácil de mantener:
fuente