varias superponer studio lineas graficos graficas c structure

studio - superponer graficas en r



¿Cuál es la necesidad de una matriz con cero elementos? (5)

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?


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 muchos códigos 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 estándar compatible habría escrito u8 data[]; y hubiera significado lo mismo. Los autores del kernel de Linux aparentemente adoran hacer cosas innecesariamente complicadas y no estándar, si se revela una opción para hacerlo.

En los viejos estándares de C, terminar una estructura con una matriz vacía se conocía como "la estructura hack". Otros ya han explicado su propósito en otras respuestas. El struct hack, en el estándar C90, era un comportamiento indefinido y podía causar bloqueos, principalmente porque un compilador de C puede agregar cualquier cantidad de bytes de relleno al final de la estructura. Dichos bytes de relleno pueden colisionar con los datos que ha intentado "hackear" al final de la estructura.

GCC desde el principio hizo una extensión no estándar para cambiar esto de comportamiento indefinido a bien definido. El estándar C99 luego adaptó este concepto y, por lo tanto, cualquier programa C moderno puede usar esta función sin riesgo. Se lo conoce como miembro de matriz flexible en C99 / C11.


Esta es una manera de tener tamaños de datos variables, sin tener que llamar a malloc ( kmalloc en este caso) dos veces. Lo usarías así:

struct bts_action *var = kmalloc(sizeof(*var) + extra, GFP_KERNEL);

Esto solía no ser estándar y se consideraba un hack (como decía Aniket), pero estaba estandarizado en C99 . El formato estándar para él ahora es:

struct bts_action { u16 type; u16 size; u8 data[]; } __attribute__ ((packed)); /* Note: the __attribute__ is irrelevant here */

Tenga en cuenta que no menciona ningún tamaño para el campo de data . Tenga en cuenta también que esta variable especial solo puede venir al final de la estructura.

En C99, este asunto se explica en 6.7.2.1.16 (el énfasis es mío):

Como un caso especial, el último elemento de una estructura con más de un miembro nombrado puede tener un tipo de matriz incompleto; esto se llama un miembro de matriz flexible . En la mayoría de las situaciones, el miembro de matriz flexible se ignora. En particular, el tamaño de la estructura es como si se hubiera omitido el miembro flexible de la matriz, excepto que puede tener más relleno posterior de lo que implicaría la omisión. Sin embargo, cuando a. (o ->) el operador tiene un operando izquierdo que es (un puntero) una estructura con un miembro de matriz flexible y el operando derecho nombra ese miembro, se comporta como si ese miembro se reemplazara con 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 matriz flexible, incluso si esto difiriera del de la matriz de reemplazo. Si esta matriz no tiene 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 pasado.

O en otras palabras, si tienes:

struct something { /* other variables */ char data[]; } struct something *var = malloc(sizeof(*var) + extra);

Puede acceder a var->data con índices en [0, extra) . Tenga en cuenta que sizeof(struct something) solo dará la contabilidad de tamaño para las otras variables, es decir, le da a los data un tamaño de 0.

También puede ser interesante observar cómo el estándar en realidad da ejemplos de malloc de tal construcción (6.7.2.1.17):

struct s { int n; double d[]; }; int m = /* some value */; struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));

Otra nota interesante del estándar en el mismo lugar es (énfasis mío):

suponiendo que la llamada a malloc tiene éxito, el objeto apuntado por p se comporta, para la mayoría de los fines, como si p hubiera sido declarado como:

struct { int n; double d[m]; } *p;

(hay circunstancias en las que esta equivalencia se rompe, en particular, las compensaciones del miembro d pueden no ser las mismas ).


Esto es un truco en realidad, para GCC ( C90 ) de hecho.

También se llama un struct hack .

Así que la próxima vez, diría:

struct bts_action *bts = malloc(sizeof(struct bts_action) + sizeof(char)*100);

Será equivalente a decir:

struct bts_action{ u16 type; u16 size; u8 data[100]; };

Y puedo crear cualquier cantidad de tales objetos struct.


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 campos de type y size ) y un miembro de data tamaño variable. Al declararlo como una matriz de 0 longitudes, se puede indexar como cualquier otra matriz. A continuación, debe asignar una estructura bts_action , por ejemplo, un tamaño de data 1024 bytes, como bts_action :

size_t size = 1024; struct bts_action* action = (struct bts_action*)malloc(sizeof(struct bts_action) + size);

Ver también: http://c2.com/cgi/wiki?StructHack


Otro uso no tan frecuente de la matriz de longitud cero es obtener una etiqueta con nombre dentro de una estructura.

Supongamos que tiene algunas definiciones de estructuras grandes (abarca varias líneas de caché) que quiere 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.

struct example_large_s { u32 first; // align to CL u32 data; .... u64 *second; // align to second CL after the first one .... };

En el código puedes declararlos usando extensiones GCC como:

__attribute__((aligned(CACHE_LINE_BYTES)))

Pero aún quiere asegurarse de que esto se aplique en tiempo de ejecución.

ASSERT (offsetof (example_large_s, first) == 0); ASSERT (offsetof (example_large_s, second) == CACHE_LINE_BYTES);

Esto funcionaría para una única 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 debe buscar los nombres del primer miembro de cada estructura:

assert (offsetof (one_struct, <name_of_first_member>) == 0); assert (offsetof (one_struct, <name_of_second_member>) == CACHE_LINE_BYTES); assert (offsetof (another_struct, <name_of_first_member>) == 0); assert (offsetof (another_struct, <name_of_second_member>) == CACHE_LINE_BYTES);

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 consistente pero que no consume ningún espacio.

#define CACHE_LINE_ALIGN_MARK(mark) u8 mark[0] __attribute__((aligned(CACHE_LINE_BYTES))) struct example_large_s { CACHE_LINE_ALIGN_MARK (cacheline0); u32 first; // align to CL u32 data; .... CACHE_LINE_ALIGN_MARK (cacheline1); u64 *second; // align to second CL after the first one .... };

Entonces, el código de aserción de tiempo de ejecución sería mucho más fácil de mantener:

assert (offsetof (one_struct, cacheline0) == 0); assert (offsetof (one_struct, cacheline1) == CACHE_LINE_BYTES); assert (offsetof (another_struct, cacheline0) == 0); assert (offsetof (another_struct, cacheline1) == CACHE_LINE_BYTES);