Утечка, вызванная

Я профилирую код игры, которую я написал, и мне интересно, как возможно, что следующий фрагмент вызывает увеличение кучи 4 кбайта (я профилирую с Heapshot Analysis Xcode) каждый раз, когда он выполняется:

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

В соответствии с профилировщиком выделенная строка выделяет 4.00Kb памяти с помощью malloc при каждом вызове функции, которая никогда не выпускается. Кажется, что это происходит с другими вызовами fread вокруг кода, но это был самый эклектичный.

Есть ли что-нибудь тривиальное, которого я не хватает? Это что-то внутреннее, о котором я не должен заботиться?

Как примечание: я профилирую его на iPhone и скомпилировал как release (-O2).

Ответ 1

Если то, что вы описываете, действительно происходит, и ваш код не имеет ошибок в другом месте, я думаю, что это ошибка в реализации.

Скорее всего, я думаю, что вы не закрываете файл. Потоки Stdio используют буферизацию по умолчанию, если устройство не является интерактивным, а буфер выделяется либо во время открытия файла, либо при выполнении ввода-вывода. Хотя нужно выделить только один буфер, вы можете, конечно, пропустить буфер, забыв закрыть файл. Но, конечно, закрытие файла должно освобождать буфер. Не забудьте проверить значение, возвращаемое fclose.

Предположим ради аргумента, что вы правильно закрываете файл, в вашем коде есть еще несколько нит, которые не будут вызывать эту проблему, но я все равно упомянул.

Сначала ваш вызов 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));
  }
}

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