c++ - void(**rptr)() 和 main() 中的调用如何在这个解决方案中工作,在 C++ 中无循环地打印 1-100?

标签 c++

在这个 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/

相关文章:

c++ - 使用 new 动态分配字符是否需要编译器将它们初始化为零

c++ - 使用 Autotools (C/C++) 生成可选的源代码?

java - JNAerator - 使用由 typedef 生成的接口(interface)

c++ - wcout 没有按要求输出

c++ - 推导 lambda 函数中的模板参数及其结果

c++ - 强制实例化派生类型而不是基类型

c++ - X 类型的值不能用于初始化 X 类型的实体

c++ - Boost.Filesystem 崩溃

c++ - 最简单的实现模式 1 writer - 多个 readers with boost library

c++ - MSVC 无法向上转换 unique_ptr