为什么这样做呢?

时间:2011-10-01 19:52:38

标签: c

#include <stdio.h>
void littledot(){}//must use C, not C++

int main() {
   littledot(568,76,105,84,116,76,101,68,111,84);
   printf("%c%c%c%c%c%c%c%c%c\n");

   getchar();
   return 0;
}

上面的代码产生结果“LiTtLeDoT”。为什么这样做?为什么568至关重要?

4 个答案:

答案 0 :(得分:7)

这在每个平台上有所不同,并且是UB(实现可以做任何它想做的事情*),但是在littledot()返回并且printf从堆栈打印这些参数后,littledot()的参数仍然在堆栈中。

永远不要依赖这个!


*真的没什么。 Afaik是一个古老的GCC版本,当它遇到一些不确定的行为时会启动一个视频游戏。

答案 1 :(得分:3)

你很幸运。这是未定义的行为,特别是对printf的调用。该计划可以做任何事情。你的实现恰好写成“LiTtLeDoT”。

确实是未定义行为的本质。编译器可以做任何想做的事情。如果你真的想知道它为什么会这样做那么你需要查看发出的目标代码。由于上述未定义的行为,查看C代码不会产生任何效果。

答案 2 :(得分:2)

http://codepad.org/tfRLaCB5

对不起,您声称要打印的程序不是我的盒子上发生的事情,而不是在键盘的盒子上发生的事情。

原因是,该程序具有未定义的行为。 printf期望格式字符串中的每个int都有一个额外的参数(%c)。你不给它那些论点,因此任何事情都可能发生。

您处于printf的某些实现,编译器选项,某些编译器和某些ABI的情况下,您最终会得到该输出。但您不应该认为任何规范都要求此输出。

答案 3 :(得分:2)

在Windows上使用Open Watcom 1.9,这对我来说是可靠的:

//must use C, not C++
#include <stdio.h>
#include <stdlib.h>

void
__stdcall // callee cleans up
littledot()
{
}

int main(void)
{
   littledot(/*568,*/'L','i','T','t','L','e','D','o','T');
   printf("%c%c%c%c%c%c%c%c%c\n");
   getchar();
   exit(0);
   return 0;
}
使用在堆栈上传递的许多参数调用

littledot() 如果littledot()的调用约定是__stdcall(或__fastcall),则必须从堆栈中删除其参数。
如果它是__cdecl,那么main()必须删除它们,但这对我们不起作用。

然而,littledot()没有也无法对参数做任何事情,因为它们没有被指定,这是你可以用C做的,但不是C ++。

所以,会发生的事情是,在调用它之后不仅littledot()的参数保留在堆栈上,而且堆栈指针也没有恢复(因为littledot()和main()都没有删除参数,通常通过调整堆栈指针来完成,堆栈指针指向'L'。然后调用printf(),首先在堆栈上放置格式字符串“%c%c%c%c%c%c%c%c%c%c \ n”的地址,从而形成函数的所有预期参数在堆栈上。 printf()愉快地打印文本并返回。

之后堆栈没有正确平衡,并且在main()中返回是有风险的,因为应用程序可能会崩溃。通过exit(0)返回修复了。

正如所有其他人所说,这一切都不能保证有效。它可能只适用于特定操作系统的特定编译器,有时仅适用。