Прежде всего, функция, выделяющая место для структуры с гибким членом массива, должна выглядеть следующим образом:
array_t* create (const size_t n, const char data[n])
{
array_t* array = malloc( sizeof(array_t) + sizeof(char[n]) );
array->length = n;
memcpy(array->data, data, n);
return array;
}
Так есть ли причина для таких объявлений массивов, если они не обеспечивают безопасность типов?
Хорошие компиляторы могут теоретически пропускать предупреждения, хотя я не думаю, что есть такие, которые это делают. Статические анализаторы будут предупреждать.
Однако основной причиной является самодокументированный код. Вы создаете тесную связь между переменной размера и переменной массива.
Может быть, мы можем написать какую-нибудь макро функцию
Конечно, со стандартным ISO C мы можем написать макрос-обертку для повышения безопасности типов и использования нотации VLA. Примерно так:
#define create_array_t(n, array) \
_Generic(&array, \
char(*)[n]: create, \
const char(*)[n]: create) (n, array)
Хитрость в том, чтобы избежать затухания массива с помощью &, чтобы получить указатель массива. Затем сравните, соответствует ли тип массива этому указателю, перед вызовом create
с переданными параметрами.
Полный пример:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
typedef struct
{
size_t length;
char data[];
} array_t;
array_t* create (const size_t n, const char data[n])
{
array_t* array = malloc(sizeof(array_t) + sizeof(char[n]));
array->length = n;
memcpy(array->data, data, n);
return array;
}
#define create_array_t(n, array) \
_Generic(&array, \
char(*)[n]: create, \
const char(*)[n]: create) (n, array)
int main (void)
{
const char a1[5];
const char a2[10];
const char *a_ptr;
(void) create_array_t(5, a1); // fine
//(void) create_array_t(5, a2); // error _Generic selector of type 'const char(*)[10]' is not compatible
//(void) create_array_t(5, a_ptr); // error _Generic selector of type 'const char**' is not compatible
return 0;
}
Это можно еще улучшить, сделав array_t
непрозрачным типом, скрыв реализацию struct внутри файла .c и получив объектно-ориентированный ADT с закрытой инкапсуляцией.