solucion - Matriz dinámica y datos precompilados
solucion general y solucion particular de sistemas de ecuaciones diferenciales lineales (4)
En C, estoy tratando de hacer lo siguiente:
typedef struct {
int length;
int items[]; /* 1 */
} wchararray_t;
typedef struct {
long hash;
wchararray_t chars; /* 2 */
} string_t;
static string_t s1 = {
617862378,
{ 5, { ''H'', ''e'', ''l'', ''l'', ''o'' } } /* 3 */
};
En palabras completas, me gustaría un tipo string_t
que termine en otro tipo wchararray_t
que tenga un tamaño dinámico, su tamaño se almacene en length
. Por otra parte, también me gustaría escribir una cadena particular precompilada, como datos estáticos, aquí s1
de longitud 5.
El código anterior asume el soporte de C99 para /* 1 */
. La inclusión de la subestructura en la estructura más grande en /* 2 */
es, según tengo entendido, no está respaldada ni siquiera por el estándar C99, pero GCC lo acepta. Sin embargo, en /* 3 */
GCC se da por vencido:
error: initialization of flexible array member in a nested context
Como solución alternativa, el código ideal anterior está escrito hasta ahora como el siguiente truco, que "tipo de trabajo":
typedef struct { int length; int items[1]; } wchararray_t;
typedef struct { long hash; wchararray_t chars; } string_t;
typedef struct { int length; int items[5]; } wchararray_len5_t;
typedef struct { long hash; wchararray_len5_t chars; } string_len5_t;
static union { string_len5_t a; string_t b; } s1 = {
617862378,
{ 5, { ''H'', ''e'', ''l'', ''l'', ''o'' } }
};
... y usaríamos "s1.b" como el string_t creado previamente (y nunca nos referiremos a "s1.a", que está aquí solo para la declaración estática de s1). Sin embargo, se rompe en el nuevo GCC 4.8, que optimiza partes de nuestro código porque, obviamente, cualquier bucle sobre los items
de un wchararray_t
puede iterar solo una vez, dado que es una matriz de longitud 1.
Este problema en particular se soluciona dando a gcc la opción -fno-aggressive-loop-optimizations
. Probablemente también se puede solucionar al no declarar la longitud en la wchararray_t
de items[]
wchararray_t
, por lo que es una matriz dinámica "solo porque". Sin embargo, esta forma de escribir código es un truco tal que preferiría una forma completamente diferente de abordar el problema ...
(Tenga en cuenta que todo es código C generado por PyPy, en lugar de código escrito a mano; cualquier cambio está bien, incluso si requiere cambiar la forma en que accedemos a los datos en cualquier lugar, siempre que las optimizaciones C "válidas" no sean válidas. prevenido.)
EDIT: reemplazó "char []" con "int []", que no acepta la sintaxis de comillas dobles "hello"
. Esto es porque estoy buscando una solución para cualquier tipo de matriz.
NO RESUELTO: gracias a todos por sus sugerencias. Parece que no hay una forma limpia, así que he implementado la solución hackish: declarar los tipos k + 1 veces, una vez con una matriz flexible "int items [];" y las k otras veces con "elementos int [N];" para los diversos valores de N que se necesitan. Esto requiere algunos trucos adicionales: por ejemplo, no usar matrices flexibles para MSVC (funcionan de manera diferente allí; no investigué para saber si funcionaría exactamente la misma sintaxis); y GCC sigue lo que dice C99 y no está contento con las estructuras que contendrían los int items[];
Como único campo. Sin embargo, es feliz si agregamos un campo ficticio char _dummy[0];
... lo que no es estrictamente C99 por lo que sé ...
Es pirata, pero ¿podría funcionar esto?
#include <stdio.h>
typedef struct {
int length;
int items[]; /* 1 */
} wchararray_t;
typedef struct {
long hash;
wchararray_t chars; /* 2 */
int dummy[]; /* hack here */
} string_t;
static string_t s1 = {
617862378, { 5 },
{ ''H'', ''e'', ''l'', ''l'', ''o'' } /* 3: changed assignment */
};
int main(void)
{
int i;
for (i=0; i < 5; ++i) {
putchar(s1.chars.items[i]);
}
putchar(''/n'');
return 0;
}
GCC me da advertencias:
xx.c:10:22: warning: invalid use of structure with flexible array member [-pedantic]
xx.c:16:9: warning: initialization of a flexible array member [-pedantic]
xx.c:16:9: warning: (near initialization for ‘s1.dummy’) [-pedantic]
Pero parece funcionar.
Edición : ¿Qué hay de agregar un "miembro de relleno" que asegure que los items[]
siempre estén alineados correctamente?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
/* change to the strictest alignment type */
typedef long aligner;
typedef struct {
long stuff; /* to show misalignment on 64-bit */
int length;
aligner padding;
int items[];
} chararray_t;
typedef struct {
long hash;
chararray_t chars;
int dummy[];
} string_t;
static string_t b1 = {
617862378,
{ 42, 5 },
{-1, -2, -3, -4, -5}
};
int main(void)
{
int i;
printf("sizeof chararray_t: %zu/n", sizeof(chararray_t));
printf("offsetof items: %zu/n", offsetof(chararray_t, items));
printf("sizeof string_t: %zu/n", sizeof(string_t));
printf("offsetof dummy: %zu/n", offsetof(string_t, dummy));
for (i=0; i < 5; ++i) {
printf("%d ", b1.chars.items[i]);
}
putchar(''/n'');
for (i=0; i < 5; ++i) {
printf("%d ", b1.dummy[i]);
}
putchar(''/n'');
return 0;
}
Cuando ejecuto lo anterior, parece que obtengo la respuesta correcta:
sizeof chararray_t: 24
offsetof items: 24
sizeof string_t: 32
offsetof dummy: 32
-1 -2 -3 -4 -5
-1 -2 -3 -4 -5
Respondiendo a mi propia pregunta para escribirla. Otro truco sería construir sobre la sugerencia de Alok, que puede dar una alineación ocasionalmente falsa, y luego corregir la alineación mediante el código de tiempo de inicio. Esto supone que la gran mayoría de los tipos utilizados en un programa se alinean correctamente. Código:
typedef struct {
long stuff; /* to show misalignment on 64-bit */
int length;
int items[];
} chararray_t;
typedef struct {
long hash;
chararray_t chars;
int dummy[];
} string_t;
static string_t b1 = {
617862378,
{ 42, 5 },
{-1, -2, -3, -4, -5}
};
/* same with b2 .. b6 */
void fixme(void) {
/* often compares as equal, and the whole function is removed */
if (offsetof(string_t, dummy) !=
offsetof(string_t, chars) + offsetof(chararray_t, items)) {
static string_t *p_array[] = { &b1, &b2, &b3, &b4, &b5, &b6 };
string_t *p;
int i;
for (i=0; i<6; i++) {
p = p_array[i];
memmove(p->chars.items, p->dummy, p->chars.length * sizeof(int));
}
}
}
Supongo que hay alguna razón para mantener la cadena "dentro" de la estructura y que desea guardar un char, no inicializando con una C-cadena.
Pero, si no, podrías hacer:
typedef struct {
int length;
char *items; /* 1 */
} chararray_t;
typedef struct {
long hash;
chararray_t chars; /* 2 */
} string_t;
static string_t s1 = {
617862378,
{ 5, "Hell" } /* 3 */
};
s1.chars.items[4] = ''o'' ;
Parece que puedes hacer el truco de la unión, pero con un encasillado en su lugar?
#include <stdio.h>
typedef struct { int length; int items[]; } wchararray_t;
typedef struct { long hash; wchararray_t chars; } string_t;
typedef struct { int length; int items[5]; } wchararray_len5_t;
typedef struct { long hash; wchararray_len5_t chars; } string_len5_t;
static union { string_len5_t a; string_t b; } s5 = {
617862378,
{ 5, { ''H'', ''e'', ''l'', ''l'', ''o'' } }
};
string_t *s1 = (string_t*) &s5 ;
int main( int argc, char *argv[])
{
for( int i = 0 ; i < s1->chars.length ; i++ )
{
printf ( "%c", s1->chars.items[i] );
}
printf( "/n" );
}
#include <stdio.h>
typedef struct {
int length;
char items[]; /* 1 */
} chararray_t;
typedef struct {
long hash;
chararray_t chars; /* 2 */
} string_t;
/*static string_t s1 = {
617862378,
{ 5, { ''H'', ''e'', ''l'', ''l'', ''o'' } } // 3
};*/
static string_t s1 =
{
617862378,
{6,"Hello"} /* 3 */
};
int main()
{
printf("%d %d %s/n",s1.hash,s1.chars.length,s1.chars.items);
return 0;
}
Añadir 1 para el carácter nulo, y voila! :)
Editar, también funciona para 2 niveles de anidamiento (GCC 4.8.0)
#include <stdio.h>
typedef struct {
int length;
char items[]; /* 1 */
} chararray_t;
typedef struct {
long hash;
chararray_t chars; /* 2 */
} string_t;
typedef struct {
long number;
string_t arr;
}experiment_t;
static experiment_t s1 =
{
617862378,
{786,{6,"Hello"}} /* 3 */
};
int main()
{
printf("%d %d %d %s/n",s1.number,s1.arr.hash,s1.arr.chars.length,s1.arr.chars.items);
return 0;
}
---------- EDIT 2 ------------------ Encontró una forma de evitar la limitación C inicializar la matriz dentro de la estructura
Código final ::
#include <stdio.h>
typedef struct {
int length;
int *items; /* 1 */
} intarray_t;
typedef struct {
long hash;
intarray_t chars; /* 2 */
int dummy[2];
} string_t;
/*string_t s1 =
{
617862378,
{
6,
{1,2,3,4,5,6}
},
{
0,0
}
};*/
string_t s1 = {617862378,{},{0,0}};
int main()
{
int i=0;
intarray_t t1 = {.length = 6, .items = (int[6]){1,2,3,4,5,6}};
s1.chars = t1;
printf("%d %d/n",s1.hash,s1.chars.length);
while(i<s1.chars.length)
{
printf("%d",s1.chars.items[i]);
i++;
}
putchar(''/n'');
return 0;
}