Основной вопрос: функция C для возврата указателя на структуру malloc - PullRequest
1 голос
/ 16 марта 2011

О структурах и указателях C ...

Вчера я написал что-то вроде следующего кода (попытайтесь запомнить его части из моей памяти):

    typedef struct {
    unsigned short int iFrames;
    unsigned short int* iTime; // array with elements [0..x] holding the timing for each frame
    } Tile;

    Tile* loadTile(char* sFile)
    {
    // expecting to declare enough space for one complete Tile structure, of which the base memory address is stored in the tmpResult pointer
    Tile* tmpResult = malloc(sizeof(Tile));

    // do things that set values to the Tile entity
    // ...

    // return the pointer for further use
    return tmpResult;
    }

    void main()
    {
    // define a tile pointer and set its value to the returned pointer (this should also be allowed in one row)
// Expected to receive the VALUE of the pointer - i.e. the base memory address at where malloc made space available
    Tile* tmpTile;
    tmpTile = loadTile("tile1.dat");

    // get/set elements of the tile
    // ...

    // free the tile
    free(tmpTile);
    }

Что я вижу: Я не могу использовать структуру malloced Tile внутри функции, но как только я пытаюсь получить доступ к ней в Main, я получаю сообщение об ошибке в куче Visual Studio (которая говорит мне, что что-то освобождается после возврата вызова).

Если я изменю его так, чтобы я выделил пространство в Main, и передал указатель на это пространство функции loadTile в качестве аргумента (чтобы функция больше ничего не возвращала), то это сработает, но я уверен, что ятакже должно быть в состоянии сделать так, чтобы функция loadTile неправильно определяла пространство и возвращала указатель на это пространство, верно?!

Спасибо !!

Ответы [ 7 ]

4 голосов
/ 16 марта 2011

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

unsigned short int* iTime; // array with elements [0..x] holding the timing for each frame

Это неправда, если вы не также не используете Malimes iTime где-то:

Tile* tmpResult = malloc(sizeof(Tile));
tmpResult->iTime = malloc(sizeof(short) * n);

Вам нужно будет освободить его, когда вы уберете:

free(tmpTile->iTime);
free(tmpTile);
2 голосов
/ 16 марта 2011

Возможно, вы перезаписываете память, которой не владеете. Я думаю, что в этом разделе:

// do things that set values to the Tile entity

вы делаете это:

tmpResult->iFrames = n;

for (i = 0 ; i < n ; ++n)
{
  tmpResult->iTime [i] = <some value>;
}

что неправильно, вам нужно выделить отдельную память для массива:

tmpResult->iTime = malloc (sizeof (short int) * n);

прежде чем писать в него. Это делает освобождение объекта более сложным:

free (tile->iTime);
free (tile);

Либо сделайте следующее:

typedef struct {
  unsigned short int iFrames;
  unsigned short int iTime [1]; // array with elements [0..x] holding the timing for each frame
} Tile;

и malloc вот так:

 tile = malloc (sizeof (Tile) + sizeof (short int) * (n - 1)); // -1 since Tile already has one int defined.

и цикл for остается прежним:

for (i = 0 ; i < n ; ++n)
{
  tmpResult->iTime [i] = <some value>;
}

но освобождение плитки - это просто:

free (tile);

так как вы выделили только один кусок памяти, а не два. Это работает, потому что C (и C ++) не выполняет проверку диапазона для массивов.

0 голосов
/ 16 марта 2011

Ваш код, с минимальными изменениями, с которыми я мог бы жить, работает для меня:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
  unsigned short int iFrames;
  unsigned short int* iTime;
} Tile;

Tile *loadTile(char* sFile) {
  Tile *tmpResult = malloc(sizeof *tmpResult);
  if (!tmpResult) return NULL;

  /* do things that set values to the Tile entity */
  /* note that iTime is uninitialized */
  tmpResult->iFrames = 42;

  (void)sFile; /* used parameter */
  return tmpResult;
}

int main(void) {
  Tile* tmpTile;
  tmpTile = loadTile("tile1.dat");
  if (!tmpTile) return 1;

  printf("value: %d\n", tmpTile->iFrames);

  free(tmpTile);
  return 0;
}
0 голосов
/ 16 марта 2011

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

Кстати, если это программа Windows, необходимо объявить main (), чтобы она возвращала int, иначе код не будет компилироваться на компиляторе C.

0 голосов
/ 16 марта 2011

Это должно работать нормально ... может быть просто предупреждение от VisualStudio о том, что вы освобождаете указатель в функции, отличной от той, в которой он был заблокирован.

0 голосов
/ 16 марта 2011

Какую бы проблему вы не испытывали, ее нет в коде, показанном в этом вопросе. Убедитесь, что вы не загромождаете указатель, прежде чем возвращать его.

0 голосов
/ 16 марта 2011

Код, который вы показали, выглядит хорошо, ошибка должна быть в коде исключено.

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