Возврат указателя структуры - PullRequest
22 голосов
/ 24 апреля 2010

Предположим, у меня есть следующая структура и функция, возвращающая указатель:

typedef struct {
  int num;
  void *nums;
  int size;
} Mystruct;

Mystruct *mystruct(int num, int size)
{ 
   //Is the following correct? Is there a more efficient way?
   Mystruct mystruct;
   mystruct.num = num;
   mystruct.size = size;
   mystruct.nums = malloc(num*sizeof(size));
   Mystruct *my;
   *my = mystruct;
   return my;
}

Я хочу определить любой указатель Mystruct, используя вышеуказанную функцию. Должен ли я объявить переменную Mystruct, определить свойства Mystruct, назначить для нее указатель и вернуть указатель или сразу определить свойства свойства mystruct через указатель?

Ответы [ 6 ]

36 голосов
/ 24 апреля 2010

Должен ли я объявить переменную Mystruct, определить свойства Mystruct, назначить на него указатель и вернуть указатель

Определенно нет, потому что переменная, определенная в функции (в классе хранения "auto"), исчезнет при выходе из функции, и вы вернете висячий указатель.

Вы можете принять указатель на Mystruct (ответственность вызывающего абонента за его распределение) и заполнить его; или вы можете использовать malloc для создания нового (вызывающий абонент обязан освободить его, когда он будет готов). Второй вариант, по крайней мере, позволяет вам сохранить сигнатуру функции, которая вам кажется интересной:

Mystruct *mystruct(int num, int size)
{
   Mystruct *p = malloc(sizeof(MyStruct));
   ....
   return p;
}

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

7 голосов
/ 24 апреля 2010

Вы не можете использовать переменную, потому что она будет освобождена при выходе из функции. Например:

Mystruct *mystruct(int num, int size)
{
   MyStruct x;
   x.num = 1;
   ...
   return &x;
}

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

Mystruct *mystruct(int num, int size)
{
   MyStruct *x;
   x = (MyStruct*)malloc( sizeof( MyStruct ) );
   x->num = 1;
   ...
   return x;
}
3 голосов
/ 24 апреля 2010

Если вы пишете общий код и не знаете, как его можно использовать, хорошо бы предоставить оба варианта:

int mystructm(Mystruct *storage, int num, int size)
{
    int rv = 0;

    storage->num = num;
    storage->size = size;
    storage->nums = malloc(num*sizeof(size));
    if (!storage->nums)
        return -1;

    return 0;
}

Mystruct *mystruct(int num, int size)
{
    Mystruct *mp = (Mystruct *)malloc(sizeof(Mystruct));
    if (mp)
    {
        if (mystructm(mp, num, size) == -1)
        {
            free(mp);
            mp = NULL;
        }
    }

    return mp;
}

Идея состоит в том, что как создатель библиотеки вы не должны диктовать политику (например, каждый Mystruct должен выделяться динамически), а должны позволять разработчику приложения решать это.

2 голосов
/ 24 апреля 2010

Еще один способ сделать это ..

int mystruct(Mystruct *mystruct, int num, int size){
   if(mystruct == NULL)
      return -1;

   mystruct->num = num;
   mystruct->size = size;
   ::
   return 0;
}

int main(){
   Mystruct my;

   if(mystruct(&my, 3, 4) != 0){
      fprintf(stderr, "Cannot init!\n");
      exit(0);
   }
   ::
}
2 голосов
/ 24 апреля 2010

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


РЕДАКТИРОВАТЬ (после редактирования исходного вопроса) Если вы отредактируете вопрос, у вас точно будут проблемы с указателем «мой». Это не инициализировано и может указывать на любую область памяти. Когда вы попытаетесь скопировать структуру в нее, вы, вероятно, получите ошибку сегмента.

2 голосов
/ 24 апреля 2010

Выделение нового Mystruct и возврат указателя на него обычно выглядят примерно так:

Mystruct *mystruct(int num, int size)
{
   Mystruct *result;

   result = malloc(sizeof(MyStruct));
   if (!result)
     return NULL;

   result->num = num;
   ...

   return result;
}

Позже, когда вы закончите с Mystruct, выделенным здесь с malloc, он должен быть снова освобожден с free().

Простое объявление локальной переменной и возвращение указателя на эту локальную переменную не сработает. Локальная переменная выходит из области видимости в конце функции, и память, в которой она хранилась, скорее всего, повторно используется для других целей. Возвращенный указатель по-прежнему будет указывать на область памяти, где была локальная переменная, но поскольку эта переменная больше не существует, этот указатель не будет иметь большого смысла.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...