Передача слишком большого количества аргументов в printf - PullRequest
30 голосов
/ 26 августа 2010

Любой программист на С, работавший более недели, сталкивался со сбоями, вызванными вызовом printf с большим количеством спецификаторов формата, чем фактическими аргументами, например:

printf("Gonna %s and %s, %s!", "crash", "burn");

Однако есть ли подобные ошибкичто может произойти, если вы передадите слишком много аргументов в printf?

printf("Gonna %s and %s!", "crash", "burn", "dude");

Мои знания по сборке x86 / x64 заставляют меня поверить, что это безвредно, хотя я не уверенчто нет какого-то краевого условия, которое я пропускаю, и я понятия не имею о других архитектурах.Это условие гарантированно безопасное, или здесь также есть потенциально опасная ловушка?

Ответы [ 5 ]

32 голосов
/ 27 августа 2010

Online C Draft Standard (n1256) , раздел 7.19.6.1, абзац 2:

Функция fprintf записывает вывод в поток, на который указывает поток, под управлением указанной строкив формат, который определяет, как последующие аргументы преобразуются для вывода.Если для формата недостаточно аргументов, поведение не определено. Если формат исчерпан, а аргументы остаются, избыточные аргументы оцениваются (как всегда), но в противном случае игнорируются. Функция fprintf возвращает, когда встречается конец строки формата.

Поведениедля всех других *printf() функций те же самые избыточные аргументы, за исключением vprintf() (очевидно).

14 голосов
/ 27 августа 2010

Вы, вероятно, знаете прототип функции printf примерно так:

int printf(const char *format, ...);

Более полная версия была бы

int __cdecl printf(const char *format, ...);

__cdecl определяет «соглашение о вызовах», которое, наряду с другими вещами, описывает, как обрабатываются аргументы. В этом случае это означает, что аргументы помещаются в стек и что стек очищается функцией, выполняющей вызов.

Одной альтернативой _cdecl является __stdcall, есть и другие. С __stdcall соглашение состоит в том, что аргументы помещаются в стек и очищаются вызываемой функцией. Однако, насколько я знаю, функция __stdcall не может принимать переменное число аргументов. Это имеет смысл, поскольку он не знает, сколько стеков нужно очистить.

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

Более подробную информацию о соглашениях о вызовах можно найти в Википедии здесь .

3 голосов
/ 27 августа 2010

printf предназначен для приема любого количества аргументов.Затем printf читает спецификатор формата (первый аргумент) и извлекает аргументы из списка аргументов по мере необходимости.Вот почему слишком мало аргументов вылетает: код просто начинает использовать несуществующие аргументы, обращаясь к памяти, которой не существует, или к чему-то другомуНо при слишком большом количестве аргументов дополнительные аргументы будут просто игнорироваться.Спецификатор формата будет использовать меньше аргументов, чем было передано.

3 голосов
/ 27 августа 2010

Все аргументы будут помещены в стек и удалены при удалении кадра стека.это поведение не зависит от конкретного процессора.(Я помню только мэйнфрейм без стека, созданный в 70-х годах). Итак, второй пример не провалится.

0 голосов
/ 27 августа 2010

Комментарий: и gcc, и clang выдают предупреждения:

$ clang main.c 
main.c:4:29: warning: more '%' conversions than data arguments [-Wformat]
  printf("Gonna %s and %s, %s!", "crash", "burn");
                           ~^
main.c:5:47: warning: data argument not used by format string 
                      [-Wformat-extra-args]
  printf("Gonna %s and %s!", "crash", "burn", "dude");
         ~~~~~~~~~~~~~~~~~~                   ^
2 warnings generated.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...