У меня есть следующее struct
с гибким элементом массива. Однако код не компилируется:
#define INITIAL_CAPACITY 5
typedef struct Object {
int size;
int elements[];
} Object;
int main(void) {
Object* result = calloc(1, sizeof(Object) + sizeof(int)*INITIAL_CAPACITY);
if (result == NULL) {
return;
}
for (int i = 0; i < INITIAL_CAPACITY; i++) {
result->elements[i] = 1;
}
result->size = INITIAL_CAPACITY;
int new_size = 2*result->size;
int* new_elements = calloc(1, sizeof(int)*new_size);
if (new_elements == NULL) {
return;
}
for (int i = 0; i < INITIAL_CAPACITY; i++) {
result[i].entries->key = -1;
}
result->elements = new_elements; // "error: expression must be modifiable lvalue"
}
Почему я не могу переназначить элемент гибкого массива новому, более крупному массиву?
ps. Он компилируется, если я вместо этого изменяю элемент гибкого массива на чтение int* elements
, но я не знаю, почему...
А если вы хотите обнулить дополнительное пространство, созданное realloc()
, вам придется использовать memset
в области между старым размером и новым размером.
Массивы не являются изменяемыми lvalue, как указано в сообщении об ошибке, поэтому вы не можете им присваивать значения. Даже если бы вы могли это сделать, значение, которое вы пытаетесь присвоить массиву, — это не массив, а указатель.
Гибкие массивы — это массивы. Массивы — это неизменяемые значения lvalue. Вы не можете присваивать один массив другому массиву.
Более того, вы сначала выделили память под целый объект типа структура, включая его гибкий массив. Новая выделенная память не содержится в объеме памяти, выделенном для объекта. Так что в любом случае ваш подход неправильный.
Элемент гибкого массива не является указателем, а является частью исходной структуры. Поскольку вы выделили структуру динамически, вам нужно использовать realloc()
, чтобы увеличить ее.
#define INITIAL_CAPACITY 5
typedef struct Object {
int size;
int elements[];
} Object;
int main(void) {
Object* result = calloc(1, sizeof(Object) + sizeof(int)*INITIAL_CAPACITY);
if (result == NULL) {
return;
}
result->size = INITIAL_CAPACITY;
int new_size = 2*result->size;
Object* new_obj = realloc(result, sizeof(Object) + sizeof(int)*new_size);
if (new_obj == NULL) {
return;
}
result = new_obj;
// zero out the new allocation, like calloc does
memset(&new_obj->elements[result->size], 0, sizeof(int)*(new_size - result->size));
result->size = new_size;
}
Возможно, также потребуется обнулить область между старым и новым размером, поскольку OP использовал calloc
.
Я добавил упоминание об этом, но оставил фактическую реализацию в качестве упражнения.
Что-то вроде memset(&new_obj->elements[result->size], 0, sizeof(int)*(new_size - result->size));
перед result->size = new_size;
.
Только что понял, что их исходный код не просто обнуляет новое распределение, он также заменяет старое распределение нулями.
Верно, хотя на самом деле они не изменили ни один из элементов, чтобы он стал ненулевым перед изменением размера. :-)
«Член гибкого массива не является указателем, он является частью исходной структуры». Спасибо-это многое проясняет.
Очевидно, что это чрезмерное упрощение, поскольку они даже не используют структуру или массив. @ИанЭбботт
@Barmar Разве элемент гибкого массива не распадается на элементы int*?
Да. Но затухание происходит только при использовании массива в качестве значения r, а не значения l.
@testing09 «Разве элемент гибкого массива не распадается на элементы int*?» Не тогда, когда это левый операнд оператора присваивания.
@IanAbbott @Barmar Я добавил еще немного кода, в котором каждому элементу массива присвоено значение 1 (намеренно не использую memcpy
). Что здесь происходит? Это структура, которая имеет указатель int (элементы) на непрерывный блок памяти, содержащий массив целых чисел, ИЛИ это структура, внутри которой нет указателя, непрерывный блок памяти, содержащий целые числа? Кроме того, не является ли мой код идиоматическим, то есть проданным, я использую int* elements
вместо гибкого элемента массива, поскольку я хочу изменить размер? Мне нужно было бы явно выделить массив — в этом случае я мог бы использовать calloc
вместо memset
.
Это похоже на структуру, которая заканчивается массивом. Единственное отличие состоит в том, что размер массива не объявлен. Он использует дополнительную память после окончания структуры, выделенную malloc()
.
@testing09 Программист должен решить, какой элемент гибкого массива или указатель наиболее подходит для конкретного случая использования.
Вы выделили всю структуру, а не только массив. Вам нужно использовать
realloc()
, чтобы увеличить все это.