Как выделить память для массива и структуры в одном вызове malloc, не нарушая строгий псевдоним?
При распределении памяти для массива с переменным размером я часто делаю что-то вроде этого:
struct array {
long length;
int *mem;
};
struct array *alloc_array( long length)
{
struct array *arr = malloc( sizeof(struct array) + sizeof(int)*length);
arr->length = length;
arr->mem = (int *)(arr + 1); /* dubious pointer manipulation */
return arr;
}
Затем я использую атрибут следующим образом:
int main()
{
struct array *arr = alloc_array( 10);
for( int i = 0; i < 10; i++)
arr->mem[i] = i;
/* do something more meaningful */
free( arr);
return 0;
}
Это работает и компилируется без предупреждений. Недавно, однако, я прочитал о строгом псевдониме. Насколько я понимаю, приведенный выше код является законным в отношении строгого сглаживания, поскольку доступ к памяти через int *
не является доступом к памяти через struct array *
. Действительно ли код нарушает строгие правила псевдонимов? Если да, то как его можно изменить, чтобы не сломать их?
Я знаю, что я мог бы распределять структуру и массив по отдельности, но тогда мне также нужно будет освобождать их отдельно, предположительно, в какой-то функции free_array
. Это означало бы, что я должен знать тип памяти, которую освобождаю, когда я ее освобождаю, что усложнит код. Вероятно, это будет медленнее. Это не то, что я ищу.
Ответы
Ответ 1
Правильный способ объявления гибкого элемента массива в структуре выглядит следующим образом:
struct array {
long length;
int mem[];
};
Затем вы можете выделить пространство по-прежнему без необходимости присваивать mem
:
struct array *alloc_array( long length)
{
struct array *arr = malloc( sizeof(struct array) + sizeof(int)*length);
arr->length = length;
return arr;
}
Ответ 2
Modern C официально поддерживает гибкие элементы массива. Таким образом, вы можете определить свою структуру следующим образом:
struct array {
long length;
int mem[];
};
И выделите его так же, как и сейчас, без лишних хлопот с сомнительной манипуляцией указателем. Он будет работать из коробки, все доступ будет правильно выровнен, и вам не придется беспокоиться о темных углах языка. Хотя, естественно, это только жизнеспособно, если у вас есть один такой член, который вам нужно выделить.
Что касается того, что у вас есть сейчас, поскольку выделенное хранилище не имеет объявленного типа (это пустой список), вы не нарушаете строгое сглаживание, так как вы не присвоили этой памяти эффективный тип. Единственная проблема заключается в возможном беспорядке выравнивания. Хотя это маловероятно с типами в вашей структуре.
Ответ 3
Я считаю, что написанный код нарушает строгие правила псевдонимов, когда стандарт читается в строгом смысле слова.
Вы получаете доступ к объекту типа int
через указатель на array
несвязанных типов. Я считаю, что простой выход - использовать начальный адрес структуры, а не преобразовать его char * и выполнить на нем арифметику указателя. Пример:
void* alloc = malloc(...);
array = alloc;
int* p_int = (char*)alloc + sizeof(array);