在这个 Quora 答案中,我偶然发现了这段代码,并想了解发生了什么:How can I print 1 to 100 in C++ without a loop, goto or recursion?
我问了我的编程老师,他说他不太熟悉 alloca()
,但他确信这个程序有未定义的行为,我最好问一下。
值得注意的是,Quora 上的答案的 OP 并不能保证这适用于其他人的系统。
我无法理解 void(**rptr)()
的作用以及 main()
中的调用如何工作,为什么 * 200
?.
#include <iostream>
#include <stdlib.h>
int num;
void(**rptr)();
void foo() {
if(num >= 100) exit(0);
std::cout << ++num << std::endl;
*rptr++ = foo;
}
int main() {
rptr = (void(**)())alloca(sizeof(*rptr) * 200) - 1;
foo();
return 0;
}
最佳答案
这是一个利用未定义行为的可怕黑客行为。分析未定义的行为毫无意义,但有时深入研究并找出确切原因很有趣。
基本上,发生的事情是 alloca(...)
正在分配足够的内存来在堆栈上存储 200 个函数指针。到目前为止,虽然不寻常,但也没什么不好的。但关键是末尾的 -1 - 它返回的内存是该存储之前的 1。所以 rptr 指向堆栈到一个未知的位置。
然后调用foo
。在foo
的末尾,我们将foo
的地址写入rptr
。但是 rptr 是有效内存之前的一个,因此我们要覆盖其他内容。
碰巧发生的其他事情是 foo
返回的返回地址(应该是 main
)。因此,它基本上“返回”到 foo
的开头,而不是返回到 main
。如此重复,直到我们到达导出。
它基本上是适度控制的堆栈粉碎。并且仅适用于具有调用约定的体系结构,其中返回地址在此庄园中放入堆栈中。
关于c++ - void(**rptr)() 和 main() 中的调用如何在这个解决方案中工作,在 C++ 中无循环地打印 1-100?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61520210/