Почему я не могу переназначить элемент гибкого массива?

У меня есть следующее 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(), чтобы увеличить все это.

Barmar 06.06.2024 19:03

А если вы хотите обнулить дополнительное пространство, созданное realloc(), вам придется использовать memset в области между старым размером и новым размером.

Ian Abbott 06.06.2024 19:08

Массивы не являются изменяемыми lvalue, как указано в сообщении об ошибке, поэтому вы не можете им присваивать значения. Даже если бы вы могли это сделать, значение, которое вы пытаетесь присвоить массиву, — это не массив, а указатель.

Ian Abbott 06.06.2024 19:14
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
57
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Гибкие массивы — это массивы. Массивы — это неизменяемые значения 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.

Ian Abbott 06.06.2024 19:09

Я добавил упоминание об этом, но оставил фактическую реализацию в качестве упражнения.

Barmar 06.06.2024 19:13

Что-то вроде memset(&new_obj->elements[result->size], 0, sizeof(int)*(new_size - result->size)); перед result->size = new_size;.

Ian Abbott 06.06.2024 19:17

Только что понял, что их исходный код не просто обнуляет новое распределение, он также заменяет старое распределение нулями.

Barmar 06.06.2024 19:21

Верно, хотя на самом деле они не изменили ни один из элементов, чтобы он стал ненулевым перед изменением размера. :-)

Ian Abbott 06.06.2024 19:23

«Член гибкого массива не является указателем, он является частью исходной структуры». Спасибо-это многое проясняет.

testing09 06.06.2024 19:24

Очевидно, что это чрезмерное упрощение, поскольку они даже не используют структуру или массив. @ИанЭбботт

Barmar 06.06.2024 19:24

@Barmar Разве элемент гибкого массива не распадается на элементы int*?

testing09 06.06.2024 19:27

Да. Но затухание происходит только при использовании массива в качестве значения r, а не значения l.

Barmar 06.06.2024 19:28

@testing09 «Разве элемент гибкого массива не распадается на элементы int*?» Не тогда, когда это левый операнд оператора присваивания.

Ian Abbott 06.06.2024 19:30

@IanAbbott @Barmar Я добавил еще немного кода, в котором каждому элементу массива присвоено значение 1 (намеренно не использую memcpy). Что здесь происходит? Это структура, которая имеет указатель int (элементы) на непрерывный блок памяти, содержащий массив целых чисел, ИЛИ это структура, внутри которой нет указателя, непрерывный блок памяти, содержащий целые числа? Кроме того, не является ли мой код идиоматическим, то есть проданным, я использую int* elements вместо гибкого элемента массива, поскольку я хочу изменить размер? Мне нужно было бы явно выделить массив — в этом случае я мог бы использовать calloc вместо memset.

testing09 06.06.2024 19:42

Это похоже на структуру, которая заканчивается массивом. Единственное отличие состоит в том, что размер массива не объявлен. Он использует дополнительную память после окончания структуры, выделенную malloc().

Barmar 06.06.2024 19:43

@testing09 Программист должен решить, какой элемент гибкого массива или указатель наиболее подходит для конкретного случая использования.

Ian Abbott 07.06.2024 11:00

Другие вопросы по теме