代码是如何工作的?

发布于 2024-10-18 22:52:15 字数 326 浏览 7 评论 0原文

下面的代码是 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

殊姿 2024-10-25 22:52:16

此代码不是合法的 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 as int main(int i), but does not set up a true prototype - so future calls to main 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 of gets and probably crash.

Although gets grabbed a line of text, this text will be ignored and discarded. This is because the return value of gets, which is &i, will be passed to scanf. So scanf reads an integer and stores it in i. No problem. scanf then returns 1, which is binary negated to some negative nonzero value, which is true, so printf 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, so gets 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 by gets.

All in all, a neat hack, but highly dependent on undefined behavior. Please don't imitate this in real code.

绮筵 2024-10-25 22:52:16

此代码的行为未定义。如果没有进一步限定,i 的类型为 intgets 期望接收可写入的 char * 缓冲区的地址,因此这会浪费内存。

The behaviour of this code is undefined. Without further qualification, i is of type int. gets is expecting to receive the address of a buffer of char * that it can write into, so this will be trashing memory.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文