值得深思……以防万一有人被旧的/错误的/低效的编译器困住,或者只是喜欢黑客攻击。
switch 语句的内部工作由两部分组成。找到要跳转的地址,然后跳转到那里。对于第一部分,您需要使用表格来查找地址。如果案例数量增加,表会变大 - 搜索地址跳转需要时间。这是编译器尝试优化的点,结合了多种技术,但一种简单的方法是直接使用表,这取决于大小写值空间。
在餐巾纸背面的例子;
switch (n) {
case 1: foo(); break;
case 2: bar(); break;
case 3: baz(); break;
}
使用这样的代码编译器可以创建一个jump_addresses数组并直接通过array[n]获取地址。现在搜索只需要 O(1)。但是如果你有一个像下面这样的开关:
switch (n) {
case 10: foo(); break;
case 17: bar(); break;
case 23: baz(); break;
// and a lot other
}
编译器需要生成一个包含 case_id、jump_address 对和代码的表来搜索该结构,最差实现可能需要 O(n)。 (体面的编译器在完全释放这种情况时会优化地狱,通过启用优化标志到一定程度,当你需要调试这种优化的代码时,你的大脑会开始煎熬。)
那么问题是你能在 C 级别自己做这一切来击败编译器吗?有趣的是,在创建表格和搜索表格时似乎很容易,在标准 C 中无法使用goto 跳转到变量点。因此,如果您由于开销或代码不打算使用函数指针,则有可能结构,你被卡住了......好吧,如果你不使用GCC。 GCC 有一个名为Labels as Values 的非标准功能,它可以帮助您获取指向标签的指针。
要完成该示例,您可以编写具有“标签作为值”功能的第二个 switch 语句,如下所示:
const void *cases[] = {&&case_foo, &&case_bar, &&case_baz, ....};
goto *labels[n];
case_foo:
foo();
goto switch_end;
case_bar:
bar();
goto switch_end;
case_baz:
baz();
goto switch_end;
// and a lot other
switch_end:
当然,如果您谈论的是 5000 个案例,最好编写一段代码来为您创建此代码 - 这可能是维护此类软件的唯一方法。
作为结束语;这会改善您的日常工作吗?不会。这会提高你的技能吗?是的,从经验来看,我曾经发现自己仅通过优化案例值就改进了智能卡中的安全算法。这是一个奇怪的世界。