2011-12-31 13 views
16

que tienen la siguiente estructuraCómo inicializar una estructura con un miembro de matriz flexible de

typedef struct _person { 
    int age; 
    char sex; 
    char name[]; 
}person; 

he hecho un poco de búsqueda de Internet básico (pero sin éxito) sobre cómo crear una instancia e inicializar una estructura con un miembro de la matriz flexibles sin usar malloc().

Por ejemplo: para estructuras normales como

struct a { 
    int age; 
    int sex; 
}; 

Podemos crear una instancia de struct a y inicializarlo como

struct a p1 = {10, 'm'}; 

Pero para estructuras con array flexibles en él (como _person como se mencionó anteriormente) ¿cómo podemos crear una instancia e inicializar como lo hacemos para structures normal?

¿Es posible? Si es así, ¿cómo pasamos el tamaño de la matriz durante la inicialización y el valor real para inicializar?

(o)

¿Es cierto que la única manera de crear una estructura con arreglo flexible está utilizando malloc() como se menciona en la especificación C99 - 6.7.2.1 Structure and union specifiers - point #17?!

+1

no puede, struct debe tener el tamaño de compilación. – Anycorn

+6

@Anycorn: las estructuras con miembros de matriz flexibles tienen un tamaño de tiempo de compilación. –

+3

GCC tiene una extensión que le permite hacer algo como 'struct {size_t len; int datos []; } x = {4, {1, 2, 3, 4}}; 'y funcionará, pero no es portátil. Siempre puedes mirar la versión de su plataforma de 'alloca' para una solución posiblemente más portátil, pero debes asegurarte de que todos se comporten de la misma manera y tengan las mismas peculiaridades de implementación. –

Respuesta

8

No, las matrices flexibles siempre se deben asignar manualmente. Pero puede usar calloc para inicializar la parte flexible y un literal compuesto para inicializar la parte fija. Me envuelvo en que una asignación inline función como esta:

typedef struct person { 
    unsigned age; 
    char sex; 
    size_t size; 
    char name[]; 
} person; 

inline 
person* alloc_person(int a, char s, size_t n) { 
    person * ret = calloc(sizeof(person) + n, 1); 
    if (ret) memcpy(ret, 
        &(person const){ .age = a, .sex = s, .size = n}, 
        sizeof(person)); 
    return ret; 
} 

Observe que esto deja la comprobación de si la asignación tuvo éxito a la persona que llama.

Si no necesita un campo size como lo incluí aquí, una macro sería suficiente. Solo que no sería posible verificar el retorno de calloc antes de hacer el memcpy. Debajo de todos los sistemas que programé hasta ahora esto abortará relativamente bien. En general, creo que return of malloc is of minor importance, pero las opiniones varían en gran medida en ese tema.

Esto podría tal vez (en ese caso especial) dan más oportunidades para el optimizador de integrar el código en el entorno:

#define ALLOC_PERSON(A, S, N)         \ 
((person*)memcpy(calloc(sizeof(person) + (N), 1),    \ 
       &(person const){ .age = (A), .sex = (S) },  \ 
       sizeof(person))) 

Editar: El caso de que esto podría ser mejor que la función es cuando A y S son constantes de tiempo de compilación.En ese caso, el compuesto literal, dado que es const calificado, podría asignarse estáticamente y su inicialización podría realizarse en tiempo de compilación. Además, si aparecen varias asignaciones con los mismos valores en el código, el compilador solo podrá realizar una única copia de ese literal compuesto.

+1

Hacer una copia no verificada del resultado de 'calloc()' es peligroso; si la asignación falla, obtienes un volcado del núcleo (u otro comportamiento indefinido que es poco probable que sea lo que querías). –

+0

@JonathanLeffler, a la derecha, se modificará. Voy a integrar eso en la función. Para el marcro solo me referiré a mi diatriba genérica sobre comprobar el retorno de 'malloc'. –

+0

Su solución de función en línea es excelente. La macro no tiene ninguna ventaja sobre ella, en mi humilde opinión. Es ilegible, no verifica el valor de retorno de calloc y no tendrá un mejor rendimiento. Por lo general, las macros no funcionan mejor que las funciones en línea (a veces peor, considere pasar strlen() a una macro, que lo evalúa dos veces). – ugoren

2

Un tipo de estructura con un miembro de matriz flexible se puede tratar como si se hubiera omitido el miembro de matriz flexible, por lo que puede inicializar la estructura de esta manera.

person p = { 10, 'x' }; 

Sin embargo, no hay miembros de la matriz flexible, asignado y cualquier intento de acceder a un miembro de la matriz flexible o formar un puntero a uno más allá de su extremo no es válido. La única forma de crear una instancia de una estructura con un miembro de matriz flexible que en realidad tenga elementos en esta matriz es asignarle memoria dinámicamente, por ejemplo con malloc.

+2

Hay una extensión GCC que le permite especificar el miembro flexible de la matriz con la misma sintaxis, si eso es lo que le interesa. –

4

Hay algunos trucos que puede usar. Depende de su aplicación particular.

Si desea inicializar una variable única, se puede definir una estructura del tamaño correcto:

struct { 
     int age; 
     char sex; 
     char name[sizeof("THE_NAME")]; 
    } your_variable = { 55, 'M', "THE_NAME" }; 

El problema es que usted tiene que utilizar la fundición puntero para interpretar la variable como "persona" (por ejemplo, . "* (* persona) (& your_variable)" Sin embargo, se puede utilizar una unión que contiene para evitar esto:.

union { 
struct { ..., char name[sizeof("THE_NAME")]; } x; 
person p; 
} your_var = { 55, 'M', "THE_NAME" }; 

así, your_var.p es de tipo "persona" también puede utilizar una macro para defina su inicializador, para que pueda escribir la cadena o Una vez:

#define INIVAR(x_, age_, sex_ ,s_) \ 
    union {\ 
    struct { ..., char name[sizeof(s_)]; } x;\ 
    person p;\ 
    } x_ = { (age_), (sex_), (s_) } 

INIVAR(your_var, 55, 'M', "THE NAME"); 

Otro problema es que este truco no es adecuado para crear una matriz de "persona". El problema con las matrices es que todos los elementos deben tener el mismo tamaño. En este caso, es más seguro usar un const char * en lugar de un char[]. O utilice la asignación dinámica;)

Cuestiones relacionadas