Возврат массива структуры с использованием указателей - PullRequest
5 голосов
/ 29 ноября 2009
    typedef struct unit_class_struct {
        char *name;
    } person;



person * setName() {
       person * array;
       array = malloc (2 * sizeof(person));

       array->name = strdup("Robert");
       array++;
       array->name = strdup("Jose");
       return array;
}


    int main()
    {
        person *array;

        array = setName();

        printf("First name is %s\n", array[0].name);
        printf("Second name is %s\n", array[1].name);

        return 0;
    }

В этом примере массив [0] .name возвращает Хосе, а не Роберта, как я ожидал, и массив [1] .name пуст.

Однако, если я использую

person * setName() {
       person * array;
       person * array_switch;
       array = malloc (2 * sizeof(person));
       array_switch = array;
       array_switch->name = strdup("Robert");
       array_switch++;
       array_switch->name = strdup("Jose");
       return array;
}

Работает как положено. array.name [0] возвращает Роберта, а array.name [1] возвращает Хосе.

Почему для работы этого примера требуется второй указатель? Могу ли я сделать это без использования второго указателя И все же использовать арифметику указателей?

Я уже знаю, что это еще один способ сделать это:

person * setName() {
       person * array;
       array = malloc (2 * sizeof(person));
       array[0].name = strdup("Robert");
       array[1].name = strdup("Jose");
       return array;
}

Ответы [ 5 ]

18 голосов
/ 29 ноября 2009

В вашем коде:

person * setName() {
   person * array;
   array = malloc (2 * sizeof(person));

   array->name = strdup("Robert");
   array++;
   array->name = strdup("Jose");
   return array;
}

Вы выделяете пространство для двух элементов в массиве и устанавливаете array для указания на первый:

+-------+      +----------+
| array | ---> | array[0] |
+-------+      +----------+
               | array[1] |
               +----------+

Затем вы увеличиваете указатель элемента на array++ и , что - это то, что возвращается в вызывающую функцию в конце. Этот указатель указывает на элемент массива second , поэтому он кажется неправильным (и почему вы почти наверняка потерпите крах при попытке освободить эту память позднее):

+-------+      +----------+
| array | -+   | array[0] |
+-------+  |   +----------+
           +-> | array[1] |
               +----------+

Что вам нужно:

person * setName() {
   person * array;
   array = malloc (2 * sizeof(person));

   array[0].name = strdup("Robert");
   array[1].name = strdup("Jose");
   return array;
}

как вы уже указали. Это решение не вообще меняет указатель array. Но, если вы действительно хотите использовать указатели, вы можете просто изменить действия array++ с помощью array-- перед возвратом, чтобы array вернулось к правильному значению:

person * setName() {
   person * array;
   array = malloc (2 * sizeof(person));

   array->name = strdup("Robert");
   array++;
   array->name = strdup("Jose");
   array--;
   return array;
}

Или, другой способ, который не меняет исходный указатель, не использует индексирование массива и не использует второй указатель, - это использование арифметики указателя. Компилятор знает, на какой тип указывает указатель, поэтому он может корректно настроить указатель для поиска следующего элемента (вы уже знаете, что он знает, как это сделать, поскольку array++ является сокращением для array = array + 1, и этот оператор корректирует значение с помощью правильного сумма также):

person * setName() {
   person * array;
   array = malloc (2 * sizeof(person));

   (array+0)->name = strdup("Robert");
   (array+1)->name = strdup("Jose");
   return array;
}
2 голосов
/ 29 ноября 2009

Вы просто выполняете простые операции арифметического указателя, такие как сложение и вычитание:

    array->name = "Robert";
    (array+1)->name = "Jose";
    return array;
1 голос
/ 29 ноября 2009

Это будет использовать арифметику указателя и сохранить исходный указатель массива:

person * setName() {
       person * array;

       array = malloc (2 * sizeof(person));
       (array+0)->name = strdup("Robert");
       (array+1)->name = strdup("Jose");

       return array;
}
1 голос
/ 29 ноября 2009

Указатель - это буквально просто число (адрес в памяти). Вы модифицируете этот указатель так, чтобы он указывал куда-то еще, а затем возвращаете его.

1 голос
/ 29 ноября 2009

Да


Поскольку вы увеличили array, у вас больше нет указателя на элемент [0], а указатель на элемент [1]. Сделайте это:

   array->name = strdup("Robert");
   array++;
   array->name = strdup("Jose");
   return array - 1;

Имейте в виду, что в C p[x] есть не что иное, как *(p + x), или, если хотите, подумайте об этом: (p + x)[0]. Этот последний случай - то, что ваша программа эффективно выполняла, с x == 1. Так что (p + 1)[1] - это то же самое, что и p[2], и там ничего нет, следовательно, ваш нулевой результат.

(Вы также можете написать это как ...

   array++->name = strdup("Robert");
   array--->name = strdup("Jose");
   return array;

... но если вы это сделаете, кучка людей придет и понизит вас. Было ли это действительно так трудно читать? Наша цель - писать только не вдохновленный неуклюжий код?)

...