什么是蹦床功能?
回答
我给你举个例子,我在网上游戏的反作弊补丁中使用。
我需要能够扫描游戏加载的所有文件以进行修改。因此,我发现要做到这一点的最强有力的方法是为CreateFileA使用蹦床。所以当游戏启动时,我会使用GetProcAddress找到CreateFileA的地址,然后修改函数的前几个字节并插入汇编代码,这些代码会跳转到我自己的“蹦床”函数,我会做一些事情,并且那么在我的jmp代码之后,我会跳回到CreateFile中的下一个位置。为了能够可靠地做到这一点有点棘手,但基本概念是钩住一个函数,强制它重定向到另一个函数,然后跳回到原始函数。
编辑:微软有一个这种类型的东西,你可以看看的框架。所谓的Detours
这里的嵌套函数的例子:
#include <stdlib.h>
#include <string.h>
/* sort an array, starting at address `base`,
* containing `nmemb` members, separated by `size`,
* comparing on the first `nbytes` only. */
void sort_bytes(void *base, size_t nmemb, size_t size, size_t nbytes) {
int compar(const void *a, const void *b) {
return memcmp(a, b, nbytes);
}
qsort(base, nmemb, size, compar);
}
compar
不能是一个外部函数,因为它使用nbytes
,其中sort_bytes
通话过程中才存在。在某些体系结构中,一个小的存根函数 - 蹦床 - 在运行时生成,并且包含调用sort_bytes
的当前的的堆栈位置。当被调用时,它跳转到compar
代码,传递该地址。
像PowerPC这样的体系结构不需要这样的混乱,ABI指定函数指针实际上是一个“胖指针”,一个包含指向可执行代码的指针和另一个指向数据的指针的结构。但是,在x86上,函数指针只是一个指针。
还有如维基百科上描述的“蹦床”的LISP感:
用在一些LISP实现中, 蹦床是一个循环,反复地 所调用咚返回功能。 A 单蹦床足以让 表达一个 节目的所有控制转移;如此表达的节目是 蹦床或“蹦床式”; 转换程序为trampolined 风格是蹦床。 Trampolined 功能可以被用来实现 尾 面向堆栈的语言
递归函数调用让我们说,我们使用JavaScript和希望写在延续传递风格天真斐波那契功能。我们这样做的原因是不相关的 - 例如将Scheme移植到JS上,或者使用我们必须使用的CPS来调用服务器端函数。
所以,第一次尝试
function fibcps(n, c) {
if (n <= 1) {
c(n);
} else {
fibcps(n - 1, function (x) {
fibcps(n - 2, function (y) {
c(x + y)
})
});
}
}
但是,随着n = 25
运行这个在Firefox提供了一个错误 '太多的递归调用!'。现在,这正是问题(缺少Javascript中的尾部调用优化),因此可以解决蹦床问题。让我们用return
指令(thunk)来调用该函数,而不是对函数进行(递归)调用,以循环方式解释。
function fibt(n, c) {
function trampoline(x) {
while (x && x.func) {
x = x.func.apply(null, x.args);
}
}
function fibtramp(n, c) {
if (n <= 1) {
return {func: c, args: [n]};
} else {
return {
func: fibtramp,
args: [n - 1,
function (x) {
return {
func: fibtramp,
args: [n - 2, function (y) {
return {func: c, args: [x + y]}
}]
}
}
]
}
}
}
trampoline({func: fibtramp, args: [n, c]});
}
对于C,蹦床将是一个函数指针:
size_t (*trampoline_example)(const char *, const char *);
trampoline_example= strcspn;
size_t result_1= trampoline_example("xyzbxz", "abc");
trampoline_example= strspn;
size_t result_2= trampoline_example("xyzbxz", "abc");
编辑:更深奥的蹦床将被隐式由编译器生成。其中一种用途就是跳转表。 (虽然显然有更复杂的,你开始尝试生成复杂的代码。)
我目前正在试验如何实现方案解释器的尾部呼叫优化,所以此刻我想图看看蹦床对我来说是否可行。
据我所知,它基本上只是一系列由蹦床功能执行的函数调用。每个函数都称为thunk,并返回计算中的下一个步骤,直到程序终止(空继续)。
下面是第一段代码,我写来提高我的蹦床的理解:
#include <stdio.h>
typedef void *(*CONTINUATION)(int);
void trampoline(CONTINUATION cont)
{
int counter = 0;
CONTINUATION currentCont = cont;
while (currentCont != NULL) {
currentCont = (CONTINUATION) currentCont(counter);
counter++;
}
printf("got off the trampoline - happy happy joy joy !\n");
}
void *thunk3(int param)
{
printf("*boing* last thunk\n");
return NULL;
}
void *thunk2(int param)
{
printf("*boing* thunk 2\n");
return thunk3;
}
void *thunk1(int param)
{
printf("*boing* thunk 1\n");
return thunk2;
}
int main(int argc, char **argv)
{
trampoline(thunk1);
}
结果:
meincompi $ ./trampoline
*boing* thunk 1
*boing* thunk 2
*boing* last thunk
got off the trampoline - happy happy joy joy !
让我补充几个例子与蹦床实现的阶乘函数,用不同的语言:
斯卡拉:
sealed trait Bounce[A]
case class Done[A](result: A) extends Bounce[A]
case class Call[A](thunk:() => Bounce[A]) extends Bounce[A]
def trampoline[A](bounce: Bounce[A]): A = bounce match {
case Call(thunk) => trampoline(thunk())
case Done(x) => x
}
def factorial(n: Int, sum: BigInt): Bounce[BigInt] = {
if (n <= 2) Done(sum)
else Call(() => factorial(n - 1, n * sum))
}
object Factorial extends Application {
println(trampoline(factorial(100000, 1)))
}
的Java:
import java.math.BigInteger;
class Trampoline<T>
{
public T get() { return null; }
public Trampoline<T> run() { return null; }
T execute() {
Trampoline<T> trampoline = this;
while (trampoline.get() == null) {
trampoline = trampoline.run();
}
return trampoline.get();
}
}
public class Factorial
{
public static Trampoline<BigInteger> factorial(final int n, final BigInteger sum)
{
if(n <= 1) {
return new Trampoline<BigInteger>() { public BigInteger get() { return sum; } };
}
else {
return new Trampoline<BigInteger>() {
public Trampoline<BigInteger> run() {
return factorial(n - 1, sum.multiply(BigInteger.valueOf(n)));
}
};
}
}
public static void main(String [ ] args)
{
System.out.println(factorial(100000, BigInteger.ONE).execute());
}
}
C(不吉利没有大的数字实现):
#include <stdio.h>
typedef struct _trampoline_data {
void(*callback)(struct _trampoline_data*);
void* parameters;
} trampoline_data;
void trampoline(trampoline_data* data) {
while(data->callback != NULL)
data->callback(data);
}
//-----------------------------------------
typedef struct _factorialParameters {
int n;
int sum;
} factorialParameters;
void factorial(trampoline_data* data) {
factorialParameters* parameters = (factorialParameters*) data->parameters;
if (parameters->n <= 1) {
data->callback = NULL;
}
else {
parameters->sum *= parameters->n;
parameters->n--;
}
}
int main() {
factorialParameters params = {5, 1};
trampoline_data t = {&factorial, ¶ms};
trampoline(&t);
printf("\n%d\n", params.sum);
return 0;
}
typedef void* (*state_type)(void);
void* state1();
void* state2();
void* state1() {
return state2;
}
void* state2() {
return state1;
}
// ...
state_type state = state1;
while (1) {
state = state();
}
// ...
- 1. 在这方面什么是蹦床?
- 2. scalaz蹦床和IO
- 3. 如何为挂钩创建蹦床功能
- 4. LLVM蹦床多少空间
- 5. jQuery AJAX蹦床重定向
- 6. 跑出0型蹦床
- 7. 为什么从PLT到GOT蹦床而不是直接跳到GOT?
- 8. 什么是免费功能?
- 9. 是什么功能NHibernate
- 10. 什么是警报功能?
- 11. I()的功能是什么?
- 12. 什么是asp.net vNext功能?
- 13. 什么是这个功能
- 14. getEvdoDbm()的功能是什么?
- 15. HttpContext.Current.Request的功能是什么?
- 16. sudo的功能是什么?
- 17. SOCK_STREAM的功能是什么?
- 18. Silverlight的功能是什么?
- 19. 什么是“页面功能”?
- 20. BDD,什么是功能?
- 21. 什么是Y功能?
- 22. 什么是功能测试?
- 23. !DOCTYPE的功能是什么?
- 24. 什么是触发功能
- 25. MonoDevelop设置修复“跑出蹦床类型2”错误
- 26. 错误消息 “跑出蹦床类型0的”
- 27. JSON.NET:用MonoTouch的不工作“跑出2型蹦床的”
- 28. SICP,延续传球风格和Clojure的蹦床
- 29. 谷歌床单,或逻辑功能
- 30. 谷歌床单定制功能内置功能
[相关](http://stackoverflow.com/questions/2420346/c-api -function-callbacks-into-c-member-function-code) – bobobobo 2010-10-12 12:26:03
它基本上是你可以用setjmp/lomgjmp实现的一些功能的一般化形式,也就是避免堆栈ovwerflow。 – Ingo 2013-07-11 15:51:23