Могут ли входные и выходные строки для sprintf() быть одинаковыми?
Я использовал этот тип соглашения много раз в моем коде в прошлом:
strcpy ( cTmpA, "hello" );
sprintf ( cTmpA, "%s world", cTmpA );
Недавно я переключил свой устаревший компилятор C на Visual Studio 2005 и обнаружил, что у меня есть искаженная строка, полученная из приведенного выше кода. Затем мне пришло в голову, что, возможно, поведение sprintf() не определено жестко, если один из входов соответствует выходной строке.
Является ли приведенный выше код действительным K & R C? Если нет, как мне найти все места в моем коде, где произошел этот тип вызова sprintf()?
Ответы
Ответ 1
Пока это действительно K & R C, вы, вероятно, хотите узнать, действительно ли он POSIX - см. спецификацию sprintf. Мы читаем:
Если копирование происходит между объектами, которые перекрываются в результате вызова sprintf() или snprintf(), результаты undefined.
Ответ 2
Большинство реализаций sprintf()
не копируют строку формата и вместо этого используют указатель внутри переданной вами строки. Если формат и вывод указывают на одну и ту же память, это приведет к нечетным результатам.
И вы действительно должны использовать snprintf()
, который защищает вас от переполнения буфера.
Чтобы найти все вызовы, поместите #define sprintf +++
в общий заголовок, найдите и перекомпилируйте все источники. Это должно дать вам список ошибок вместе с именем файла и номерами строк:) Или использовать рекурсивный поиск в вашей среде IDE.
Если вы хотите обрезать этот список до тех, где вы используете один и тот же указатель для обоих аргументов, используйте этот макрос:
#define sprintf(output,format,...) check_sprintf(__FILE__,__LINE__,output,format,....)
Обратите внимание, что не все компиляторы поддерживают макросы с помощью varargs. Затем определите новую функцию check_sprintf
:
int check_sprintf (char*filename,int line,char*output,char*format,...) {
va_list args;
int len;
if(output==format) {
fprintf(stderr,
"Output and format are the same at %s:%d", filename, line);
abort();
}
va_start (args, format);
len = vsprintf (output, format, args);
va_end (args);
return len;
}
[EDIT] Я только что увидел, что вы говорите о выходе и первом аргументе. Вы можете повторно использовать код сверху и вызвать va_arg()
, чтобы получить первый аргумент и использовать его в сравнении.