向printf传递了太多参数

JSB*_*ոգչ 31 c printf

任何已经工作超过一周的C程序员遇到了因printf使用更多格式说明符而非实际参数调用而导致的崩溃,例如:

printf("Gonna %s and %s, %s!", "crash", "burn");
Run Code Online (Sandbox Code Playgroud)

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

printf("Gonna %s and %s!", "crash", "burn", "dude");
Run Code Online (Sandbox Code Playgroud)

我对x86/x64程序集的了解使我相信这是无害的,虽然我不相信没有一些边缘条件我缺少,而且我不知道其他架构.这种情况是否保证是无害的,或者这里是否存在潜在的崩溃诱因?

Joh*_*ode 33

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

fprintf函数将输出写入stream指向的流,在指向格式的字符串的控制下,该格式指定后续参数如何转换为输出.如果格式的参数不足,则行为未定义.如果参数保留时格式已用尽,则会评估多余的参数(一如既往),否则将被忽略.当遇到格式字符串的末尾时,fprintf函数返回.

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

  • 这应该是公认的答案!谁在乎这里召集会议?这只是实施标准的一个细节. (2认同)

tor*_*rak 15

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

int printf(const char *format, ...);
Run Code Online (Sandbox Code Playgroud)

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

int __cdecl printf(const char *format, ...);
Run Code Online (Sandbox Code Playgroud)

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

另一种选择_cdecl__stdcall,还有其他的.根据__stdcall惯例,参数被压入堆栈并由被调用的函数清理.但是,据我所知,__stdcall函数不可能接受可变数量的参数.这是有道理的,因为它不知道要清理多少堆栈.

它的长期和短期是,在__cdecl函数的情况下,它可以安全地传递你想要的多个args,因为清理是在进行调用的代码中执行的.如果你以某种方式向__stdcall函数传递太多参数,则会导致堆栈损坏.可能发生这种情况的一个例子是你有错误的原型.

有关调用约定的更多信息可以在维基百科上找到.

  • -1表示MS-isms,好像它们是C语言的一部分. (9认同)
  • __cdecl是一个Win32ism,由一些旧的DOS编译器支持C和pascal调用约定这一事实创建. (6认同)
  • @ ninjalj,`__cdecl`仅受MS编译器支持,但有关调用约定的一般说明对所有操作系统均有效。 (2认同)
  • @ninjalj:基于68000的Macintosh操作系统几乎可以使用"Pascal"调用约定(称为函数弹出堆栈).实际上有点讽刺,因为在68000上调用约定需要一个类似的序列:"mov.l(A7 +),A0/addq#4,A7/jmp(A0)",而C调用约定允许使用"返回"指令. (2认同)