跳转表/分支与解除引用函数指针是一回事吗?

时间:2017-07-22 10:54:24

标签: c++ c function pointers jump-table

我一直在努力学习跳桌是什么,而我却无法理解某些东西。从我看过的很多例子来看,它们似乎已经归结为这个,或者至少这是它的一个版本:

void func1() {};
void func2() {};
void func3() {};

int main()
{
    void(*jumpTo[3])(void) = { func1, func2, func3 };
    jumpTo[1]();

    return 0;
}

这些似乎是一个函数指针数组,它们由某个值/位置索引。那么跳转表只是索引一个函数指针数组吗?我对此非常好奇,因为我看到很多人都说switch语句经常被编译成跳转表作为性能指标。根据我的理解,通过以这种方式跳转到函数,它涉及指针取消引用和函数调用。 我认为这两者对表现来说并不是那么好。

该网站的另一个答案是,通过这种方式“你正在添加一个switch语句不一定具有的函数调用开销”。编译成跳转表的开关如何避免函数调用?

此外,一个高度投票的答案说:“跳转表可以是函数指针数组或机器代码跳转指令数组。” 您将如何跳转到机器代码指令而不是取消引用指针?这更快吗?

在上面的例子中,两者之间的区别是指针不必被取消引用,因为它可以静态绑定吗?而不是在运行时传入一个随机数作为索引?

感谢。

3 个答案:

答案 0 :(得分:3)

跳转表和函数表基本相同 - 地址数组。跳转表包含public function insertsingle() { $string = "INSERT INTO academic (name,username,pw) VALUES (?, ?, ?)"; $stmt = mysqli_prepare($this->con, $string); $stmt->bind_param("sss", $this->getName(), $this->getUserName(), $this->getPassword()); $rsint = $stmt->execute(); return $rsint; } - 目标的地址。 两者之间的唯一区别是跳跃是如何进行的。当一个函数被调用时,返回地址被压入堆栈,所以当函数终止时它可以返回。

这是一个跳转表的例子:

goto

这编译为:

#include <stdio.h>
int main(int argc, char *argv[])
{
    switch (argc)
    {
        case 1: 
             printf("You provided no arguments.");
             break;
        case 2: 
             printf("You provided one argument.");
             break;
        case 3: 
             printf("You provided two arguments.");
             break;
        case 4: 
             printf("You provided three arguments.");
             break;
        case 5: 
             printf("You provided four arguments.");
             break;
        case 6: 
             printf("You provided five arguments.");
             break;
        default: 
             printf("You provided %d arguments.", argc-1);
             break;
    }
    return 0;
}

答案 1 :(得分:1)

主要区别在于对于跳转表,您通常可以使用相对于程序计数器的寻址,这样表不需要任何重定位,并且可以存在.text部分(或其他部分,不可写和共享)。这是因为典型的跳转表仅在同一目标文件中的极少数位置使用,并且汇编器已知所有偏移量。

如果你有一个函数指针数组,那么你需要生成真正的指针,这需要某种形式的重定位。

第二种可能性,即跳转指令数组,并不仅限于跳转指令本身。重要的是所有目标指令序列(除了最后一个)具有相同的长度,因此可以容易地计算跳转到的偏移量。这样,不需要跳转表,但它确实需要精确的指令宽度(和计数)信息,这在大多数目标上很难保证(RISC架构在加载常量时可能难以预测有效的指令数量) )。这意味着在实践中,这种方法仅限于针对目标的非常特定的跳转指令形式。

答案 2 :(得分:1)

通常,术语跳转表是指其中存在多于2个分支/跳跃目标的技术,并且通过以一种方式或另一种方式计算表中的位置来通过变量来选择跳跃/分支目标。实质上,您提供的示例:

void(*jumpTo[3])(void) = { func1, func2, func3 };
jumpTo[1]();

整体上是使用跳转表 - 而不仅仅是函数指针的取消引用。

C也提供其他机制 - 例如,交换机案例通常被编译成跳转表,特别是如果案例值具有窄范围,并且两者之间的间隙很小。 GCC提供的另一种机制是use of goto labels as pointer values with computed goto