Объединить обозначенные инициализаторы и malloc в C99 +? - PullRequest
8 голосов
/ 01 сентября 2011

Есть ли хороший способ объединить назначенные инициализаторы из C99 с результатом malloc?

Кажется, что следующее дублирование не нужно:

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

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

Можно ли использовать Type и *t из вышеуказанного кода?

Ответы [ 3 ]

5 голосов
/ 22 сентября 2011

Так как вы спросили;) в C есть один инструмент для избежания явного дублирования кода, макросы.Тем не менее, я не вижу способа не повторять хотя бы название типа.Но в C ++ они тоже не могут, поэтому C, по крайней мере, так же хорош:)

Самое простое, что я вижу, это

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

, что даст

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

это имеет несколько преимуществ.

  • Он правильно инициализирует все элементы, даже на архитектурах с нестандартными представлениями 0 для типов с плавающей точкой или указателей.
  • Кроме версии Keith 'itявляется приемлемым «стилем кодирования», так как это просто выражение, которое выглядит как инициализация, и любой должен немедленно визуально захватить то, что должен делать второй фрагмент кода.

Примечание: соблюдайте const вмакрос, это позволяет сложить несколько экземпляров составного литерала, если компилятор решит, что это актуально.Также есть способы иметь вариант, в котором список указателей является необязательным, см. P99 ниже.

Недостатком является memcpy, и я был бы счастлив с назначением.Во-вторых, нет проверки на malloc перед использованием результата, но, возможно, можно столкнуться с какой-то странностью, чтобы код красиво завершился.

В P99 Я немного идудругой путь.Там у нас всегда есть функция инициализации для типа, что-то вроде

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;
}

, которая может быть использована с помощью магии макроса для предоставления аргументов по умолчанию для a, b и c, если они опущены.Тогда вы можете просто использовать что-то вроде

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

в своем коде приложения.Это лучше, поскольку позволяет избежать разыменования указателя при сбое вызова malloc.С другой стороны, это вновь вводит порядок инициализаторам, поэтому тоже не идеален.

2 голосов
/ 01 сентября 2011

Вы можете с помощью макроса variadic.Я не собираюсь утверждать, что это хорошая идея, но она работает:

#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;
}

Обратите внимание, что я не смог использовать обычный трюк определения макроса do { ... } while (0) (это создаст новыйscope, а t не будет видно), поэтому вам следует быть осторожным с контекстом, в котором вы используете это.

Лично я думаю, что я счастлив от ненужного дублирования.

1 голос
/ 01 сентября 2011

Нет, это единственный способ использовать назначенные инициализаторы. Без (Type) {} компилятор не знает, как проверять содержимое.

...