Объединить назначенные инициализаторы и malloc в C99+?

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

Следующее кажется ненужным дублированием:

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

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

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


person Matt Joiner    schedule 01.09.2011    source источник
comment
Если бы вы могли, что бы произошло, если бы malloc вернул NULL?   -  person detly    schedule 01.09.2011
comment
Похоже, вы действительно хотите C++.   -  person Steve-o    schedule 01.09.2011
comment
Интересный вопрос, но я не думаю, что есть хороший ответ. Лично я бы просто использовал calloc, а затем t->a=2; t->b=3; ... (calloc просто там, если вы хотите пропустить какие-либо члены, поэтому, если вы знаете, что вы явно установите их все, вы можете использовать malloc точно также)   -  person R.. GitHub STOP HELPING ICE    schedule 01.09.2011


Ответы (3)


Поскольку вы спросили;) в 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 для типов с плавающей запятой или указателей.
  • В отличие от версии Кейта, это приемлемый «стиль кодирования», поскольку это просто выражение, похожее на инициализацию, и любой должен немедленно визуально зафиксировать, что должен делать второй фрагмент кода.

NB: обратите внимание на 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. С другой стороны, это снова вводит порядок в инициализаторы, что тоже не идеально.

person Jens Gustedt    schedule 22.09.2011
comment
Ваш макрос DESIGNATE_NEW наиболее близок к идиоматическому решению. - person Matt Joiner; 05.10.2011

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

#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) (он создал бы новую область видимости, а t не был бы виден), поэтому вам нужно быть осторожным с контекстом, в котором вы используете это.

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

person Keith Thompson    schedule 01.09.2011

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

person chmeee    schedule 01.09.2011