2011-09-01 8 views
6

¿Hay alguna manera de combinar los inicializadores designados de C99 con el resultado de malloc?¿Combinar inicializadores designados y malloc en C99 +?

El siguiente parece tener la duplicación innecesaria:

typedef struct { 
    int a, b, c; 
} Type; 

Type *t = malloc(sizeof *t); 
*t = (Type) { 
    .a = 2, 
    .b = 3, 
    .c = 5, 
}; 

¿Puede la utilización de Type y *t ser retirado del código de seguridad?

+3

Si pudiera, ¿qué pasaría si 'malloc' devolviera' NULL'? – detly

+0

Parece que realmente quieres C++. –

+0

Pregunta interesante, pero no creo que haya una buena respuesta. Personalmente, simplemente usaría 'calloc' seguido de' t-> a = 2; t-> b = 3; '... (' calloc' está ahí en caso de que quiera omitir miembros, por lo que si sabe que los configurará explícitamente, también podría usar 'malloc') –

Respuesta

5

Dado que usted lo pidió;) hay una herramienta en C para evitar la duplicación explícita de código, macros. Dicho esto, no veo una manera de no repetir al menos el nombre del tipo. Pero en C++ no pueden tampoco, por lo que C es al menos tan bueno :)

El más fácil de lo que veo es

#define DESIGNATE_NEW(T)   \ 
    memcpy(malloc(sizeof(T)),   \ 
     &(T const){ __VA_ARGS__ }, \ 
     sizeof(T)) 

que daría

Type *t = DESIGNATE_NEW(Type, 
    .a = 2, 
    .b = 3, 
    .c = 5, 
); 

esto tiene varias ventajas.

  • inicializa todos los miembros correctamente, incluso en arquitecturas con representaciones no estándar de la 0 para los tipos de flotador o punteros.
  • Aparte de la versión de Keith, es aceptable el "estilo de codificación", ya que es solo una expresión que se parece a una inicialización y cualquiera debe capturar de forma visual lo que se supone que debe hacer el segundo snipset de código.

NB: Observar la const en la macro, esto permite que varias instancias del compuesto literal a ser doblados, si el compilador decide que esto sea relevante. También hay medios para tener una variante donde la lista de designadores es opcional, ver P99 a continuación.

La desventaja es el memcpy y estaría más feliz con una tarea. En segundo lugar, no hay verificación de falla del malloc antes de usar el resultado, pero uno podría encontrar algunas rarezas para que el código salga bien.

En P99 voy un poco diferente. No siempre tenemos una función de inicialización para un tipo, algo así como

inline 
Type* Type_init(Type* t, int a, int b, int c) { 
    if (t) { 
    *t = (Type const){ .a = a, .b = b, .c = c }; 
    } 
    return t; 
} 

la que por arte de magia macro se puede hacer para proporcionar argumentos predeterminados para a, bc y si se omiten.Luego puede simplemente usar algo como

Type *t = P99_NEW(Type, 1, 2, 3); 

en el código de la aplicación. Esto es mejor, ya que evita la desactivación del puntero cuando falló la llamada a malloc. Por otro lado, esto reintroduce un orden para los inicializadores, por lo que tampoco es perfecto.

+0

Su macro 'DESIGNATE_NEW' se acerca más a ser una solución idiomática. –

1

No, esa es la única forma de usar los inicializadores designados. Sin (Tipo) {}, el compilador no sabe cómo validar los contenidos.

2

Puedes hacerlo con una macro variable. No voy a decir que esto es una buena idea, pero funciona:

#include <stdlib.h> 
#include <stdio.h> 

#define CREATE(type, ptr, ...) \ 
    type *ptr = malloc(sizeof *ptr); \ 
    if (ptr) *ptr = (type){__VA_ARGS__} 

int main(void) 
{ 
    typedef struct { 
     int a, b, c; 
    } Type; 
    CREATE(Type, t, .a = 2, .b = 3, .c = 5); 
    printf("t->a = %d, t->b = %d, t->c = %d\n", t->a, t->b, t->c); 
    return 0; 
} 

Tenga en cuenta que yo no era capaz de utilizar el habitual do { ... } while (0) definición truco macro (que crearía un nuevo ámbito, y t no sería visible), por lo que tendría que tener cuidado con el contexto en el que usa esto.

Personalmente, creo que estoy más feliz con la innecesaria duplicación.