10

我正在分析我编写的游戏的代码,我想知道以下代码段每次执行时如何导致堆增加 4kb(我正在使用 Xcode 的 Heapshot Analysis 进行分析):

u8 WorldManager::versionOfMap(FILE *file)
{
  char magic[4];
  u8 version;

  fread(magic, 4, 1, file); <-- this is the line
  fread(&version,1,1,file);
  fseek(file, 0, SEEK_SET);

  return version;
}

根据分析器,突出显示的行在malloc每次调用函数时分配 4.00Kb 的内存,内存永远不会被释放。这件事似乎发生fread在对代码的其他调用中,但这是最令人欣喜的一个。

有什么微不足道的我想念的吗?这是我不应该关心的内部问题吗?

就像一个注释:我在 iPhone 上对其进行分析,它被编译为发布 ( -O2)。

4

1 回答 1

3

如果您所描述的事情确实发生了并且您的代码在其他地方没有错误,我认为这是实现中的错误。

我认为更有可能是您没有关闭文件。如果设备是非交互式的,则 Stdio 流默认使用缓冲,并且在打开文件时或执行 I/O 时分配缓冲区。虽然只应分配一个缓冲区,但您肯定会因为忘记关闭文件而泄漏缓冲区。但当然,关闭文件应该释放缓冲区。不要忘记检查返回的值fclose

假设为了论证您正确地关闭了文件,您的代码中有几个其他的 nit 不会导致这个问题,但我还是会提到。

首先,您的fread调用读取一个具有一个大小为 4 的成员的对象。您实际上有一个具有 4 个大小为 1 的成员的对象。换句话说,fread交换了数字参数。这仅在返回值的含义上有所不同(在部分读取的情况下很重要)。

其次,虽然您第一次调用fread正确地将大小硬编码char为 1(在 C 中,即“大小”的定义),但sizeof(u8)在第二次调用fread.

如果这确实是内存泄漏的想法是正确的解释(并且其他地方没有任何错误),那么您可以通过关闭此特定文件的 stdio 缓冲来解决该问题:

bool WorldManager::versionOfMap(FILE *file, bool *is_first_file_io, u8 *version)
{
  char magic[4];
  bool ok = false;
  if (*is_first_file_io) 
  {
    // we ignore failure of this call
    setvbuf(file, NULL, _IONBF, 0);
    *is_first_file_io = false;
  }

  if (sizeof(magic) == fread(magic, 1, sizeof(magic), file) 
      && 1 == fread(version, sizeof(*version), 1, file))
  {
      ok = true;
  }
  if (-1 == fseek(file, 0L, SEEK_SET))
  {
      return false;
  }
  else
  {
      return ok && 0 == memcmp(magic, EXPECTED_MAGIC, sizeof(magic));
  }
}

即使我们假设这确实是一个错误,并且泄漏是真实的,也值得将您的代码压缩为仍然可以证明问题的最小示例。如果这样做揭示了真正的错误,你就赢了。否则,您将需要最小的示例来报告实现中的错误。

于 2012-04-14T19:56:41.753 回答