# V668. Possible meaningless check for null, as memory was allocated using 'new' operator. Memory allocation will lead to an exception.

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

Если оператор 'new' не смог выделить память, то согласно стандарту языка Си++, генерируется исключение std::bad_alloc(). Таким образом проверять указатель на равенство нулю не имеет смысла. Рассмотрим простейший случай:

    MyStatus Foo()
    {
      int *p = new int[100];
      if (!p)
        return ERROR_ALLOCATE;
      ...
      return OK;
    }

Указатель 'p' никогда не будет равен нулю. Функция никогда не вернет константное значение ERROR_ALLOCATE. Если выделить память невозможно, то возникнет исключение. Исправим код наиболее простым образом:

    MyStatus Foo()
    {
      try
      {
        int *p = new int[100];
        ...
      }
      catch(const std::bad_alloc &)
      {
        return ERROR_ALLOCATE;
      }
      return OK;
    }

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

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

    // For each processor; spawn a CPU thread to access details.
    hThread = new HANDLE [nProcessors];
    dwThreadID = new DWORD [nProcessors];
    ThreadInfo = new PTHREADINFO [nProcessors];
    // Check to see if the memory allocation happenned.
    if ((hThread == NULL) ||
        (dwThreadID == NULL) ||
        (ThreadInfo == NULL))
    {
      char * szMessage = new char [128];
      sprintf(szMessage,
              "Cannot allocate memory for "
              "threads and CPU information structures!");
      MessageBox(hDlg, szMessage, APP_TITLE, MB_OK|MB_ICONSTOP);
      delete szMessage;
      return false;
    }

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

Частой причиной описанных проблем, является изменение в поведении оператора 'new'. Во времена Visual C++ 6.0, оператор 'new' в случае ошибки возвращал NULL. Следующие версии Visual C++ следуют стандарту и генерируют исключение. Помните про это изменение поведения. Таким образом, если вы адаптируете старый проект для сборки современным компилятором, вам стоит уделить повышенное внимание диагностике V668.

Примечание N1. Анализатор не будет выдавать предупреждение, если используется placement new или "new (std::nothrow) T". Пример такого кода:

    T * p = new (std::nothrow) T; // OK
    if (!p) {
      // An error has occurred.
      // No storage has been allocated and no object constructed.
      ...
    }

Примечание N2. Есть возможность слинковать проект с nothrownew.obj. В этом случае, оператор new не будет генерировать исключение. Такой возможностью, например, пользуются разработчики драйверов. Подробнее: new and delete operators. В этом случае, просто отключите предупреждение V668.

Дополнительные материалы по данной теме:

- Wikipedia. Placement syntax.
- Microsoft Support. Operator new does not throw a bad_alloc exception on failure in Visual C++.
- Stack Overflow. Will new return NULL in any case?

Данная диагностика классифицируется как:

CWE-570 CWE-571 CERT-MEM52-CPP

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V668.