Возврат массива структуры с использованием указателей

StackOverflow https://stackoverflow.com/questions/1814463

Вопрос

    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;
    }

В этом примере array [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;
}
Это было полезно?

Решение

В вашем коде:

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 ++ и that - это то, что возвращается вызывающей функции в конце. Этот указатель указывает на элемент массива 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;
}

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

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

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

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

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

person * setName() {
       person * array;

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

       return array;
}

Да

<Ч>

Поскольку вы увеличили 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;

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top