C4996 (функция небезопасная) предупреждение для strcpy, но не для memcpy

Я пишу код в VS2010, и я вижу, что после компиляции компилятор дает мне предупреждение C4996 ( "Эта функция или переменная может быть небезопасной" ) для вызовов strcpy и sprintf.

Однако я не мог получить похожие предупреждения для memcpy (и, возможно, в коде есть несколько подобных "небезопасных" вызовов функций)

int _tmain(int argc, _TCHAR* argv[])
{
    char buf1[100], buf2[100];
    strcpy (buf1, buf2); // Warning C4996 displayed here asking to use strcpy_s instead
    memcpy (buf1, buf2, 100); // No warning here asking to use memcpy_s
    memcpy_s(buf1, 100, buf2, 100);
    return 0;
}

Почему это так? Как включить предупреждение C4996 для всех возможных небезопасных вызовов в моем коде?

Ответ 1

В общем, для компиляции кода C вам нужен соответствующий C-компилятор. Visual Studio является несоответствующим компилятором С++.

Вы получаете предупреждение, потому что Visual Studio плохо. См. это.

C4996 появляется всякий раз, когда вы используете функцию, которую Microsoft считает устаревшей. По-видимому, Microsoft решила, что они должны диктовать будущее языка C, а не рабочую группу ISO C. Таким образом, вы получаете ложные предупреждения для совершенно точного кода. Проблема заключается в компиляторе.

Нет ничего плохого в функции strcpy(), которая является мифом. Эта функция существует около 30-40 лет, и каждая ее часть должным образом документирована. Итак, что делает функция и что она не делает, это не должно быть неожиданностью, даже для начинающих программистов на C.

Что strcpy делает и не делает:

  • Он копирует строку с нулевым завершением в другую ячейку памяти.
  • Он не несет никакой ответственности за обработку ошибок.
  • Он не исправляет ошибки в приложении-вызывающем.
  • Он не несет никакой ответственности за обучение программистов C.

Из-за последнего замечания выше вы должны знать следующее перед вызовом strcpy:

  • Если вы передаете строку неизвестной длины в strcpy, не проверяя ее длину заранее, у вас есть ошибка в приложении-вызывающем.
  • Если вы передаете некоторый фрагмент данных, который не заканчивается на \0, у вас есть ошибка в приложении-вызывающем.
  • Если вы передадите два указателя на strcpy(), которые указывают на перекрывающиеся ячейки памяти, вы вызываете поведение undefined. Это означает, что у вас есть ошибка в приложении-получателе.

Например, в коде, который вы опубликовали, вы никогда не инициализировали массивы, поэтому ваша программа, скорее всего, сработает и сгорит. Эта ошибка никоим образом не связана с функцией strcpy() и не будет решена путем замены strcpy() на что-то еще.

Ответ 2

strcpy небезопасно, если отсутствует завершающий NUL, так как он может копировать больше символов, чем соответствовать области назначения. При memcpy количество скопированных байтов фиксировано.

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

Ответ 3

Вы получаете это предупреждение, потому что не передавать длину строки и полагаться на завершение \0 небезопасны, поскольку они могут вызвать переполнение буфера. В memcpy вы проходите длину, чтобы не переполнять проблему.

Вы можете использовать что-то вроде

#ifdef _MSC_VER
#  pragma warning(push)
#  pragma warning(disable:4996)
#endif
strcpy... ;  // Code that causes unsafe warning
#ifdef _MSC_VER
#  pragma warning(pop)
#endif

Если вы не беспокоитесь о переносимости, вы можете использовать такие альтернативы, как strcpy_s и т.д.

Ответ 4

Поскольку strcpy и sprintf действительно являются небезопасными функциями, это зависит от содержимого строки, а не от переполнения. Вместо этого вы должны использовать strncpy и snprintf, чтобы убедиться, что он не перезаписывает память.

Пока memcpy не в этом случае, он имеет длину, поэтому он не перезаписывает память, пока длина правильная.

Ответ 5

Включить в заголовок "stdafx.h" определение

#define _CRT_SECURE_NO_WARNINGS

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

Ответ 6

Предупреждение означает, что функция устарела и не будет доступна в будущих версиях: http://msdn.microsoft.com/en-US/en-en/library/ttcz0bys.aspx Вы не можете добавить другие функции в список отказов Microsoft.

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

Ответ 7

Причина, по которой вы получаете предупреждение на sprintf и strcpy, а не на memcpy, заключается в том, что memcpy имеет параметр длины, который ограничивает количество копий памяти. Для strcpy и memcpy вход должен быть завершен с помощью \0. Если нет, это будет продолжаться за гранью. Вы можете ограничить это, используя функции snprintf и strncpy. Они неявно ограничивают, сколько можно скопировать.

Обратите внимание, что microsoft устарело snprintf, поэтому вместо этого вы должны использовать функцию замены _snprintf. Однако это специфическая функция MSVC.

Я бы посоветовал покончить с буферами char * и переключиться на С++, используя stl-контейнер, например std::string. Это сэкономит вам массу отладочных головных болей и сохранит ваш код портативным.