代码是如何工作的?
下面的代码是 C 语言。当给定一组输入数字时,它会跳过第一个数字,并打印其余的数字。
main(i)
{
if(~scanf("%d",gets(&i)))
printf("%d\n",i),main();
}
我想知道,这段代码是如何工作的?
编辑:对于那些这么认为的人 不起作用 http://www.ideone.com/cENzy
The code below is in C. When given a set of input numbers, it skips the first number, and prints the rest.
main(i)
{
if(~scanf("%d",gets(&i)))
printf("%d\n",i),main();
}
I would like to know, how does this code work?
EDIT: For those of you, who think it
does not work
http://www.ideone.com/cENzy
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
此代码不是合法的 C。
main
必须采用零个、两个或三个参数。一个参数不是合法的选项。获取
堆栈上的涂鸦。坦率地说,如果这真的有效的话那就是一个奇迹——未定义的行为比比皆是!话虽如此,让我们看看 C 代码在 x86 上编译的典型方式,以了解其工作原理。首先,
main(i)
是一个古老的 K&R 风格的声明。它被解释为int main(int i)
,但没有设置真正的原型 - 因此将来对main
的调用不会对其参数进行类型检查。回想一下,在 x86 上,参数是通过在调用目标函数之前将它们压入堆栈来传递的。因此,如果我们的参数数量错误,它不会崩溃(假设您使用的是这种 ABI!),而只是提供虚假数据。另请注意,在 x86 上,堆栈向下增长 - 当您调用函数时,当前堆栈指针减少。这意味着,如果您破坏了这些参数之一以上的内存,您将破坏属于调用函数的内存,并且可能直到您返回才注意到。
现在让我们看看执行流程。
gets(&i)
首先执行,并且(假设编译器忽略类型不匹配!)获取一行文本,并将其存储到堆栈上,覆盖调用者的堆栈帧!< /em> 这假设堆栈在内存中向下增长;在向上增长的堆栈上,根据字符串的长度,这将覆盖gets
的返回地址,并可能崩溃。虽然
gets
抓取了一行文本,但是该文本将被忽略并丢弃。这是因为gets
的返回值,即&i
,将被传递给scanf
。因此scanf
读取一个整数并将其存储在i
中。没问题。scanf
然后返回1
,它是二进制非零负值,这是真的,因此printf
然后打印该值。然后,逗号运算符用于再次递归调用 main,并使用错误数量的参数(参数通常会使用一些虚假值进行初始化),这充当循环。请注意,在
scanf
返回后,输入中仍保留未使用的换行符,因此gets
会在下次处理该换行符。另请注意,当 EOF 发生时,scanf
将返回 EOF (0xFFFFFFFF),该值在逻辑上会被否定为 0。main
随后将返回,并立即崩溃,因为其调用者的堆栈已可能已被gets
覆盖。总而言之,这是一个巧妙的技巧,但高度依赖于未定义的行为。请不要在实际代码中模仿这一点。
This code is not legal C.
main
must take either zero, two, or three parameters. One parameter is not a legal option.gets
scribbles over the stack. Quite frankly, it's a miracle if this works at all - undefined behavior abounds!With that said, let's look at the typical way C code is compiled on x86 to understand how this works. First,
main(i)
is an old, K&R-style declaration. It is interpreted asint main(int i)
, but does not set up a true prototype - so future calls tomain
won't have their arguments typechecked. Recall that on x86, arguments are passed by pushing them to the stack just before calling the target function. So if we have the wrong number of arguments, it won't crash (assuming you're on this kind of ABI!), but simply give bogus data.Also note that on x86, the stack grows downward - as you call functions, the current stack pointer decreases. This means that if you corrupt memory above one of these parameters, you'll be corrupting memory belonging to the calling function, and might not notice until you return.
Now let's look at the flow of execution.
gets(&i)
is executed first, and (assuming the compiler ignores the type mismatch!) obtains a line of text, and stores it onto the stack overwriting the caller's stack frame! This assumes a stack that grows downward in memory; on upward-growing stacks this will, depending on the length of the string, overwrite the return address ofgets
and probably crash.Although
gets
grabbed a line of text, this text will be ignored and discarded. This is because the return value ofgets
, which is&i
, will be passed toscanf
. Soscanf
reads an integer and stores it ini
. No problem.scanf
then returns1
, which is binary negated to some negative nonzero value, which is true, soprintf
then prints the value. The comma operator then serves to recursively call main again, with the wrong number of arguments (the argument will typically be initialized with some bogus value), which acts as a loop.Note that after
scanf
returns, a newline remains in the input unconsumed, sogets
deals with that next time around. Also note that when EOF occurs,scanf
will return EOF (0xFFFFFFFF), which will be logically negated to 0.main
will then return, and promptly crash because its caller's stack has probably been overwritten bygets
.All in all, a neat hack, but highly dependent on undefined behavior. Please don't imitate this in real code.
此代码的行为未定义。如果没有进一步限定,
i
的类型为int
。gets
期望接收可写入的char *
缓冲区的地址,因此这会浪费内存。The behaviour of this code is undefined. Without further qualification,
i
is of typeint
.gets
is expecting to receive the address of a buffer ofchar *
that it can write into, so this will be trashing memory.