sprintf s с слишком маленьким буфером

следующий код вызывает ошибку и убивает мою заявку. Это имеет смысл, поскольку буфер имеет длину всего 10 байт, а текст-22 байта (переполнение буфера).

char buffer[10];    
int length = sprintf_s( buffer, 10, "1234567890.1234567890." ); 

Как мне поймать эту ошибку, чтобы я мог сообщить об этом вместо сбоя моего приложения?

Edit:

после прочтения комментариев ниже я пошел с _snprintf_s. Если он возвращает значение -1, то буфер не был обновлен.

length = _snprintf_s( buffer, 10, 9, "123456789" );
printf( "1) Length=%dn", length ); // Length == 9

length = _snprintf_s( buffer, 10, 9, "1234567890.1234567890." );
printf( "2) Length=%dn", length ); // Length == -1

length = _snprintf_s( buffer, 10, 10, "1234567890.1234567890." );
printf( "3) Length=%dn", length ); // Crash, it needs room for the NULL char 

6 ответов


вместо sprintf_s, вы могли бы использовать snprintf (a.к. а _snprintf на windows).

#ifdef WIN32
#define snprintf _snprintf
#endif

char buffer[10];    
int length = snprintf( buffer, 10, "1234567890.1234567890." );
// unix snprintf returns length output would actually require;
// windows _snprintf returns actual output length if output fits, else negative
if (length >= sizeof(buffer) || length<0) 
{
    /* error handling */
}

это по дизайну. Весь смысл sprintf_s, и другие функции от *_s семья, чтобы поймать ошибки переполнения буфера и рассматривать их как нарушений условие. Это означает, что они не предназначены для восстановления. Это предназначено только для ловли ошибок - вы никогда не должны звонить sprintf_s Если вы знаете, что строка может быть слишком большой для целевого буфера. В этом случае используйте strlen сначала проверьте и решите, нужно ли вам обрезать.


это работает с VC++ и даже безопаснее, чем использование snprintf (и, конечно, безопаснее, чем _snprintf):

void TestString(const char* pEvil)
{
  char buffer[100];
  _snprintf_s(buffer, _TRUNCATE, "Some data: %s\n", pEvil);
}

флаг _TRUNCATE указывает, что строка должна быть усечена. В этой форме размер буфера фактически не передается, что (парадоксально!) что делает его таким безопасным. Компилятор использует магию шаблонов для вывода размера буфера, что означает, что он не может быть неправильно указан (удивительно распространенная ошибка). Этот метод может быть применен для создания других безопасных string wrappers, как описано в моем блоге здесь: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/


из MSDN:

другое основное различие между sprintf_s и sprintf заключается в том, что sprintf_s принимает параметр length, определяющий размер выходного буфера в символах. Если буфер слишком мал для печатаемого текста, буфер устанавливается в пустую строку и вызывается обработчик недопустимого параметра. В отличие от snprintf, sprintf_s гарантирует, что буфер будет завершен нулем (если размер буфера не равен нулю).

так идеально то, что вы написали должно работать правильно.


Похоже, вы пишете на MSVC какого-то рода?

Я думаю, что документы MSDN для sprintf_s говорят, что он утверждает, умирает, поэтому я не слишком уверен, что вы можете программно поймать это.

Как LBushkin предложил, Ты гораздо лучше использовать классы, которые управляют за ниточки.


см. раздел 6.6.1 на TR24731 что это комитет ISO C версия функциональности, реализованной Microsoft. Он предоставляет функции set_constraint_handler(), abort_constraint_handler() и ignore_constraint_handler() функции.

есть комментарии Павла Минаева, предполагающие, что реализация Microsoft не соответствует предложению TR24731 (которое является "техническим отчетом типа 2"), поэтому вы не сможете вмешаться, или вам, возможно, придется сделать что-то отличное от того, что TR указывает, что должно быть сделано. Для этого внимательно изучите MSDN.