【问题标题】:Basic question: C function to return pointer to malloc'ed struct基本问题:返回指向 malloc 结构的指针的 C 函数
【发布时间】:2011-03-16 14:51:08
【问题描述】:

关于 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 函数 malloc 空间并返回指向该空间的指针吧?!

谢谢!!

【问题讨论】:

  • void main ... RAAAAARRRRGGGGGHHHHH
  • 请告诉我们有bug的部分代码

标签: c pointers struct


【解决方案1】:

您正在尝试做的事情没有任何问题,或者至少不是这里的代码。但是,我担心这条线:

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

除非您在某处分配 iTime,否则这不是真的:

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

你需要在清理时释放它:

free(tmpTile->iTime);
free(tmpTile);

【讨论】:

  • ... 并将free 逻辑放在一个单独的函数free_tile 中,这样您就不会将瓷砖处理客户端代码与Tile 结构的内部联系起来。 +1。
【解决方案2】:

您可能正在写入您不拥有的内存。我猜在本节中:

// 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++)不对数组进行范围检查。

【讨论】:

  • 我认为你的意思是 tmpResult->iTime = malloc (sizeof (short) * n)。不过很酷的黑客!值得指出的是,这仅在动态大小的数组是结构中的最后一项时才有效。
【解决方案3】:

你的代码,尽可能少的改变,对我有用:

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

【讨论】:

    【解决方案4】:

    您显示的代码看起来不错,错误一定在省略的代码中。

    【讨论】:

      【解决方案5】:

      无论您遇到什么问题,它都不在此问题中显示的代码中。确保在返回之前没有破坏指针。

      【讨论】:

        【解决方案6】:

        这应该可以正常工作...可能只是来自 VisualStudio 的警告,您正在释放与分配到不同函数中的指针。

        【讨论】:

          【解决方案7】:

          从技术上讲,您的代码可以在 C 编译器上运行。但是,在函数内部动态分配并返回指向已分配数据的指针是造成内存泄漏的极好方法——因此这是非常糟糕的编程实践。更好的方法是在调用者(在本例中为 main)分配内存。分配内存的代码单元应该与释放它的代码单元相同。

          顺便说一句,如果这是一个 Windows 程序,main() 必须声明为返回 int,否则代码将无法在 C 编译器上编译。

          【讨论】:

          • 返回malloc'd 内存是一种非常常见的做法,与您的建议相比,它大大减少了耦合。定义 foo *make_foo()free_foo 是 C 中 foo 构造/初始化和销毁​​的惯用方法。
          • @larsmans 我想我们在这里说的是同样的事情。为了减少耦合,你应该大致像这两个例子一样:stackoverflow.com/questions/5309245/c-char-question/…
          • 在我看来,您在main 中推荐了Tile *p = malloc(sizeof(Tile)); init_tile(p);
          • @larsmans 该语法也没有任何问题,具体取决于功能。如果函数是“用数据缓冲区做事的东西”,那么执行任务的函数不应该与 malloc() 相关。例如,整个 Windows 都是以这种方式构建的:将指针和缓冲区大小传递给函数。现在,当然,如果数据的性质是 main() 不需要关心它,它应该被私有地封装在分配数据的模块中。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-21
          • 1970-01-01
          • 2018-12-03
          相关资源
          最近更新 更多