Присвоение адреса члену массива указателей структуры в C - PullRequest
2 голосов
/ 17 октября 2010

Имеются значительные проблемы с арифметикой некоторых указателей. Я думаю Я получаю понятия (переменные указателя указывают на адрес памяти, нормальные переменные указывают на данные), но я считаю, что моя проблема с синтаксисом (*, &, (*), *(), и т. Д.)

То, что я хочу сделать, это создать динамические массивы пользовательской структуры (т.е. массивы указателей на структуры кучи), и мой интерфейс предоставляет два метода, "ad_to_obj_array" (который принимает добавляемый объект и массив, который может быть нулевым для пустого) и "obj_array_dustbin" (который просто принимает массив для удаления, также удаляя содержимое, куча объектов). Первый отображается ниже.

Детали объектов не важны (и структура была переименована в любом случае), но мое решение общей проблемы приведено ниже, и я был бы признателен, если вы обнаружите ошибку. Компилятор жалуется на недопустимое lvalue, где я пытаюсь назначить адрес в указателе на RHS значению указателя в массиве указателей на структуры кучи:

#define NUM_ELEM(x) (sizeof (x) / sizeof (*(x)))

obj* add_to_obj_array(obj* new_obj, obj* array)
{
  int number_of_elements = 0;
  if (array != NULL)
  {
    number_of_elements = NUM_ELEM(array);
  }

  obj* new_array = NULL;

  /* note: I am expecting sizeof(new_obj) to return the size of an obj* 
     to go into the array of pointers. */
  if ( NULL ==
       (new_array = (obj*)malloc((number_of_elements + 1)* sizeof(new_obj))) )
  {
    /* memory request refused :( */
    return NULL;
  }

  /* copy the old array pointers into the new array's pointer slots: */
  int i;
  for (i = 0; i < number_of_elements; i++)
  {
    &(new_array[i]) = &(array[i]);
  }

  /* add the new item to the end (assign pointer value directly): */
  new_array[number_of_elements] = new_obj;

  if (number_of_elements > 0)
  {
    free(&array);
  }

  return new_array;
}

Теперь я попробовал следующие перестановки ошибочной строки:

  &(new_array[i]) = &(array[i]);
  *(new_array[i]) = &(array[i]);
  new_array[i] = &(array[i]);

и все они дают ошибку компилятора того или иного рода. Я вполне уверен, что правая часть является адресом i-го элемента старого массива, но как мне назначить i-й элемент нового массива, когда элементы массива являются указателями на структуры?

РЕДАКТИРОВАТЬ - обратите внимание, что макрос NUM_ELEM выше НЕ РАБОТАЕТ; он всегда вернется 1. Посмотрите ответ @Merlyn Morgan-Graham ниже, чтобы узнать почему.

Ответы [ 4 ]

8 голосов
/ 17 октября 2010

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

Сейчас вы уже определили new_array(и, предположительно, array) в качестве указателя на obj.Результат выглядит следующим образом:

alt text

В этом случае у вас есть указатель на динамически размещенный массив объектов.Когда / если вы расширяете выделение, вам нужно будет скопировать все объекты сами.

Согласно вашему описанию: «(т.е. массивы указателей на структуры кучи)», вам нужен массив указателей,Если вы хотите выделить этот массив указателей автоматически, ваше определение будет выглядеть так:

obj *array[NUMBER];

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

alt text

В этом случае new_array и array каждый должен быть определен как указатель на указатель на obj.Затем вы должны выделить массив указателей (т. Е. Указателей на столько obj с, сколько хотите), и у каждой точки будет точка obj:

obj **new_array;

// allocate an array of pointers with space to point at more items:    
new_array = malloc(sizeof(obj *) * new_elements);

// copy the pointers to the current items to the new array:
for (i=0; i<current_elements; i++)
    new_array[i] = array[i];

Преимущество этого состоит в том, чтокогда вы выполняете копирование, вы копируете только указатели , а не сами объекты.Особенно с большими объектами, это может сэкономить значительное количество усилий.Компромисс заключается в том, что использование элемента проходит через два уровня косвенного целочисленного значения, равного одному, поэтому ссылка может быть медленнее (хотя редко намного медленнее, особенно на относительно высокопроизводительном процессоре).

Как уже заметил @rerun, в любом случае вы, вероятно, захотите использовать realloc.В частности, этот может быть в состоянии расширить распределение "на месте" и избежать частого копирования данных.Конечно, это не гарантировано, но, по крайней мере, вы даете этому шанс;если вы malloc и копируете каждый раз, вы исключаете даже возможность такой оптимизации.

1 голос
/ 17 октября 2010

У вас есть два массива, не new_array[i] = array[i] делает то, что вам нужно.

  • Вы рассматривали realloc как возможное решение.
0 голосов
/ 17 октября 2010

в вашем коде элементы массива не являются указателями на структуру, они являются объектами структуры.элементы массива obj ** массива this являются указателями на структуру obj.

#define NUM_ELEM(x) (sizeof (x) / sizeof (*(x)))

void add_to_obj_array(obj* new_obj, obj** array)
{
  int number_of_elements = 0;
  if (array != NULL)
  {
    number_of_elements = NUM_ELEM(array);
  }

  // expand array with one more item
  array = (obj**)realloc(array, (number_of_elements + 1) * sizeof(new_obj));

  if (array == NULL )
  {
    /* memory request refused :( */
    return;
  }

  // Put new item at the last place on the array
  array[number_of_elements] = new_obj;
}

Поэтому здесь мы использовали матрицу (указатель на указатели структуры obj).Когда мы добавляем новый элемент, мы просто расширяем существующий массив на одно место, и на этом месте мы помещаем новый объект структуры.Нет необходимости возвращать значение, потому что мы работаем с указателями объектов, и все изменения выполняются на реальных объектах, а не на их копиях.

0 голосов
/ 17 октября 2010

Просто назначьте значения через.new_array[i] = array[i].

Проблема, с которой вы можете столкнуться, заключается в том, что для obj* фактически является массивом указателей , obj должен сам по себе иметь тип указателя:

typedef struct
{
  int value1;
} obj_pool;

typedef obj_pool* obj;

int main(int argc, char* argv[])
{
  obj_pool pool1;
  pool1.value1 = 5;
  obj array[] = { &pool1 };
  array[0]->value1 = 16;
  return 0;
}

Другая проблема, с которой вы столкнетесь после получения этой компиляции, - sizeof(array) == sizeof(obj*).NUM_ELEM(array) будет всегда возвращать одно и то же значение.Это означает, что вам придется передать параметр size_t array_size в вашу функцию.

...