C语言 将太多参数传递给 printf

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3578970/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 06:18:49  来源:igfitidea点击:

Passing too many arguments to printf

cprintf

提问by JSB????

Any C programmer who's been working for more than a week has encountered crashes that result from calling printfwith more format specifiers than actual arguments, e.g.:

任何已经工作了一个多星期的 C 程序员都遇到过由于printf使用比实际参数更多的格式说明符进行调用而导致的崩溃,例如:

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

However, are there any similar bad things that can happen when you pass too manyarguments to printf?

但是,当您向 printf传递太多参数时,是否会发生类似的坏事?

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

My knowledge of x86/x64 assembly leads me to believe that this is harmless, though I'm not convinced that there's not some edge condition I'm missing, and I have no idea about other architectures. Is this condition guaranteed to be harmless, or is there a potentially crash-inducing pitfall here, too?

我对 x86/x64 汇编的了解使我相信这是无害的,尽管我不相信我没有遗漏某些边缘条件,而且我不知道其他架构。这种情况是否保证是无害的,或者这里是否也存在潜在的导致崩溃的陷阱?

采纳答案by torak

You probably know the prototype for the printf function as something like this

你可能知道 printf 函数的原型是这样的

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

A more complete version of that would actually be

一个更完整的版本实际上是

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

The __cdecldefines the "calling convention" which, along with other things, describes how arguments are handled. In the this case it means that args are pushed onto the stack and that the stack is cleaned by the function making the call.

__cdecl定义了“调用约定”,它与其它东西一起,介绍了参数是如何处理的。在这种情况下,这意味着 args 被压入堆栈,并且堆栈由进行调用的函数清除。

One alternative to _cdeclis __stdcall, there are others. With __stdcallthe convention is that arguments are pushed onto the stack and cleaned by the function that is called. However, as far as I know, it isn't possible for a __stdcallfunction to accept a variable number of arguments. That makes sense since it wouldn't know how much stack to clean.

一种替代方法_cdecl__stdcall,还有其他方法。随着__stdcall该约定是参数被压入堆栈,并通过调用的函数清理。但是,据我所知,__stdcall函数不可能接受可变数量的参数。这是有道理的,因为它不知道要清理多少堆栈。

The long and the short of it is that in the case of __cdeclfunctions its safe to pass however many args you want, since the cleanup is performed in the code makeing the call. If you were to somehow pass too many arguments to a __stdcallfunction it result in a corruption of the stack. One example of where this could happen is if you had the wrong prototype.

总而言之,在__cdecl函数的情况下,无论您想要传递多少参数都是安全的,因为清理是在进行调用的代码中执行的。如果您以某种方式向__stdcall函数传递太多参数,则会导致堆栈损坏。可能发生这种情况的一个例子是,如果您使用了错误的原型。

More information on calling conventions can be found on Wikipedia here.

有关调用约定的更多信息可以在此处的Wikipedia 上找到

回答by John Bode

Online C Draft Standard (n1256), section 7.19.6.1, paragraph 2:

在线 C 草案标准 (n1256),第 7.19.6.1 节,第 2 段:

The fprintf function writes output to the stream pointed to by stream, under control of the string pointed to by format that specifies how subsequent arguments are converted for output. If there are insufficient arguments for the format, the behavior is undefined. If the format is exhausted while arguments remain, the excess arguments are evaluated (as always) but are otherwise ignored.The fprintf function returns when the end of the format string is encountered.
fprintf 函数将输出写入流指向的流,在格式指向的字符串的控制下,格式指定如何转换后续参数以供输出。如果格式的参数不足,则行为未定义。如果格式已用完而参数仍然存在,则对多余的参数进行评估(一如既往),否则将被忽略。fprintf 函数在遇到格式字符串的结尾时返回。

Behavior for all the other *printf()functions is the same wrt excess arguments except for vprintf()(obviously).

所有其他*printf()函数的行为与多余的参数相同,除了vprintf()(显然)。

回答by Ned Batchelder

printfis designed to accept any number of arguments. printf then reads the format specifier (first argument), and pulls arguments from the argument list as needed. This is why too few arguments crash: the code simply starts using non-existent arguments, accessing memory that doesn't exist, or some other bad thing. But with too many arguments, the extra arguments will simply be ignored. The format specifier will use fewer arguments than have been passed in.

printf旨在接受任意数量的参数。printf 然后读取格式说明符(第一个参数),并根据需要从参数列表中提取参数。这就是为什么太少的参数会崩溃:代码只是开始使用不存在的参数,访问不存在的内存,或者其他一些不好的事情。但是如果参数太多,多余的参数就会被忽略。格式说明符将使用比传入的更少的参数。

回答by stacker

All the arguments will be pushed on the stack and removed if the stack frame is removed. this behaviour is independend from a specific processor. (I only remember a mainframe which had no stack, designed in 70s) So, yes the second example wont't fail.

如果删除堆栈帧,所有参数都将被压入堆栈并删除。此行为与特定处理器无关。(我只记得 70 年代设计的没有堆栈的大型机)所以,是的,第二个示例不会失败。

回答by jfs

Comment: both gcc and clang produce warnings:

评论: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.