向 printf 传递太多参数
任何已经工作了一周以上的 C 程序员都遇到过由于使用比实际参数更多的格式说明符调用 printf
导致的崩溃,例如:
printf("Gonna %s and %s, %s!", "crash", "burn");
但是,当您通过printf 的参数太多?
printf("Gonna %s and %s!", "crash", "burn", "dude");
我对 x86/x64 汇编的了解使我相信这是无害的,尽管我不相信我没有遗漏某些边缘条件,而且我对其他架构一无所知。这种情况是否保证是无害的,或者这里是否也存在潜在的导致崩溃的陷阱?
Any C programmer who's been working for more than a week has encountered crashes that result from calling printf
with more format specifiers than actual arguments, e.g.:
printf("Gonna %s and %s, %s!", "crash", "burn");
However, are there any similar bad things that can happen when you pass too many arguments to 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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在线 C 草案标准 (n1256),部分7.19.6.1,第 2 段:
除
vprintf()
之外(显然),所有其他 *printf() 函数的行为与多余参数相同。Online C Draft Standard (n1256), section 7.19.6.1, paragraph 2:
Behavior for all the other
*printf()
functions is the same wrt excess arguments except forvprintf()
(obviously).您可能知道 printf 函数的原型如下所示
更完整的版本实际上是
__cdecl 定义了“调用约定”,它与其他内容一起描述了如何处理参数。在这种情况下,这意味着 args 被推入堆栈,并且堆栈由进行调用的函数清理。
_cdecl
的一种替代方法是__stdcall
,还有其他方法。对于__stdcall
,约定是参数被推入堆栈并由调用的函数清理。但是,据我所知, __stdcall 函数不可能接受可变数量的参数。这是有道理的,因为它不知道要清理多少堆栈。总而言之,在 __cdecl 函数的情况下,可以安全地传递您想要的任意多个参数,因为清理是在调用的代码中执行的。如果您以某种方式将太多参数传递给 __stdcall 函数,则会导致堆栈损坏。可能发生这种情况的一个例子是如果您使用了错误的原型。
有关调用约定的更多信息可以在维基百科此处找到。
You probably know the prototype for the printf function as something like this
A more complete version of that would actually be
The
__cdecl
defines 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.One alternative to
_cdecl
is__stdcall
, there are others. With__stdcall
the 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__stdcall
function to accept a variable number of arguments. That makes sense since it wouldn't know how much stack to clean.The long and the short of it is that in the case of
__cdecl
functions 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__stdcall
function it result in a corruption of the stack. One example of where this could happen is if you had the wrong prototype.More information on calling conventions can be found on Wikipedia here.
如果移除堆栈帧,则所有参数将被压入堆栈并被移除。此行为独立于特定处理器。 (我只记得一个没有堆栈的大型机,设计于 70 年代)所以,是的,第二个例子不会失败。
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.
printf
旨在接受任意数量的参数。然后 printf 读取格式说明符(第一个参数),并根据需要从参数列表中提取参数。这就是为什么太少的参数会崩溃:代码只是开始使用不存在的参数,访问不存在的内存,或者其他一些坏事。但如果参数太多,多余的参数就会被忽略。格式说明符将使用比传入的参数更少的参数。printf
is 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.评论: gcc 和 clang 都会产生警告:
Comment: both gcc and clang produce warnings: