0

我真的很惊讶我找不到这个问题已经问过了。我想知道 switch 语句占用多少代码空间,以及使用 const 查找表是否会更有效地满足我的需求。

    typedef struct container{
    type1 a;
    type2 b;
    type3 c;
}container;

static container d;
//option A
void foo(int num)
{
    void* x;
    switch (num)
    {
    case 1:
      x = &d->a;
      break;
    case 2:
      x = &d->b;
      break;
    case 3:
      x = &d->c;
      break;
    default:
      x = NULL;
      break;
    }
    // do something with x
}
// option B
const void* lookup_table[] = {
    d.a,
    d.b,
    d.c,
    NULL
};

void foo(int num)
{
    void* x = lookup_table[num];
    // do something with x
}

switch 语句如何分解为程序集,它在代码空间中会大多少?是否值得使用查找表而不是使用 switch 语句?

4

4 回答 4

2

如果您可以将开关重写为对查找表的简单查找,那可能是最好的解决方案,尤其是在可能的索引很密集的情况下,因为它也可能更具可读性。(如果可能的索引不密集,您可以浪费空间或使用更复杂的查找技术:两级表、哈希表、二进制搜索到排序列表。这些可能比 switch 语句更好,但会更少可读。)不过,一个好的编译器会努力匹配效率,其中一些会产生与您完全相同的代码。

但在通常情况下,您需要的不仅仅是查找一个值,switch 语句几乎肯定会更好。一个好的编译器会将 switch 语句编译成上述策略之一,并且考虑到目标平台的详细信息,它可能比你更了解最佳解决方案。

特别是,将 switch 语句转换为函数指针的索引查找,然后通过函数指针调用可能比 switch 语句慢得多,因为调用函数的开销很大。使用 switch 语句,编译器很可能会生成一个分支表,其中查找代码将与您手动构建的代码非常相似,但查找后所做的是一个简单的分支而不是函数调用。

于 2015-11-17T20:13:09.323 回答
1

看帖子的另一种方式:

void foo(int num) { void* x; switch (num)...很好地应对num范围外1,2,3

void foo(int num) { void* x = lookup_table[num];num当超出 的范围时具有未定义的行为0,1,2,3


有些人可能会说num范围不是问题。但这并没有在帖子中说明。代码维护也是如此——许多未说明的、隐含的、有时是错误假设的条件。

是否值得使用查找表而不是使用 switch 语句?

为了值得维护,我会选择switch().

于 2015-11-17T21:09:47.800 回答
1

这个问题没有确切的意义。优化编译器(通常)至少一次编译整个函数(通常是整个翻译单元)。

阅读R.Sayle 关于编译开关的这篇论文。您将了解到有几种竞争策略(跳转表、平衡树、条件移动、哈希跳转表等),其中一些可以组合使用。

相信您的优化编译器会做出足够好的选择来编译您的开关代码。对于GCC,如果您想查看生成的汇编程序,gcc -Wall -O2 -march=native可能会添加-fverbose-asm -S(和/或替换-O2为)进行编译。-O3还可以了解gcc -flto -O3等...

当然,出于基准测试和生产代码的目的,您应该始终要求您的编译器进行优化。

请注意,作为扩展( Clang/LLVM也接受...)GCC 将标签作为值(带有间接gotos)。有了它们,你可以强制使用跳转表,或者有一些线程代码。这并不总是让你的代码更快(例如因为分支预测)。

于 2015-11-17T20:12:50.177 回答
0

正如其他人已经说过的那样,现代优化编译器将尝试自己选择一个好的策略来将开关编译成更高效的代码。Hans Wennborg 在 2015 LLVM 开发者会议上发表了关于最近开关降低改进的演讲,简要介绍了该主题。

因此,最好让编译器完成其工作并决定最易读的解决方案,而不是您认为最有效的解决方案。

如果您想查看 Clang 为您的 switch 文件生成的代码,您可以使用-S-S -emit-llvm

于 2015-11-18T08:02:42.863 回答