我正在阅读C language
中的一些文字。文中说switch{} case
只能接受整数类型。
我很好奇为什么switch{} case
不接受其他类型,如float或string。这背后有什么理由吗?
非常感谢。
答案 0 :(得分:18)
经典原因可能是对于整数值“决策表达式”,可以进行非常好的优化。
基本上,您可以将case语句列表映射到包含地址的表,然后根据值直接跳转。显然,对于浮点数和字符串不起作用。
在GCC中,您可以使用some extensions手动执行此操作:
const char * digit_name(int d)
{
const void * handlers[] = { &&zero, &&one, &&two, &&three, &&four,
&&five, &&six, &&seven, &&eight, &&nine };
goto *handlers[d]; /* Assumes d is in range 0..9. */
zero: return "zero";
one: return "one";
two: return "two";
three: return "three";
four: return "four";
five: return "five";
six: return "six";
seven: return "seven";
eight: return "eight";
nine: return "nine";
return NULL;
}
这通常被称为“计算goto”,并且应该清楚switch
如何基本上可以编译成非常相似的东西。对已打开表达式的严格定义有助于,例如使用enum
。
此外,C在语言层面上并没有太多的字符串概念。
答案 1 :(得分:3)
C的语言哲学就是你所看到的就是你得到的。没有隐藏的机制。这实际上是该语言的强大力量之一。
启用整数涉及预期的分支,而在float和string上进行比较会产生隐藏成本。
答案 2 :(得分:3)
我会回答一个问题:你为什么使用switch语句而不是if else else?
令人惊讶的是,许多程序员从不问这个问题,而是将switch
视为语言必须存在的基本内容。那不是真的!您可以在不使用switch
的情况下编写任何类型的C程序。严格来说,switch
是一项冗余功能。
那么为什么要用呢?
可读性不是 的原因。与{if-else相比,switch
实际上具有更糟糕且更不直观的语法。在switch内部需要break语句,switch的奇怪语法规则允许在另一个case的本地范围内声明case,默认的任意位置。
switch
不仅可读性较差,而且比if-else更容易出错。被遗忘的休息是最明显的危害,导致数百万难以找到的软件漏洞。
另一个更明显的反对switch
更具可读性的论点是这个“裸骨”代码:
if (A)
{
}
else if (B)
{
}
else if (C)
{
}
else
{
}
switch(something)
{
case A:
{
break;
}
case B:
{
break;
}
case C:
{
break;
}
default:
{
break;
}
}
上面的if和开关在语法和功能上是等价的,应该编译成完全相同的机器代码。以下是此示例中的统计信息
if-else switch
Symbols 33 65 // Not counting the expressions
Lines 12 19 // Not counting empty lines
switch需要更多代码才能获得相同的结果,因此必须将其视为 less 可读而不是if-else。
在你开始争论开关看起来更像是一个表而不是if-else之前,那就是代码格式化,而且无关紧要。标准中没有任何内容阻止您编写如下代码:
if (A) {}
else if (B) {}
else if (C) {}
else {}
switch(something)
{
case A: { break; }
case B: { break; }
case C: { break; }
default: { break; }
}
如果您希望使用某种类似于表格的最小语法,我会认为这两种形式都是可读的。
当然可能存在美学,迷信或宗教原因,为什么switch
应该用于可读性,但我宁愿将这些非主题讨论留给非编程相关网站。
因此,与if-else相比,switch的安全性和可读性更低。那些可能对程序员有吸引力的是效率。一个必须由程序测试的具有 n 案例的程序员肯定希望能够尽快找到能够快速搜索正确案例的东西。以线性方式检查所有案例是一个坏主意。
正如您所知,通过将其实现为函数指针数组,可以优化if-else或切换很多:
typedef void (*Func_t)(void);
const Func_t cases [N] = { ... };
cases[i]();
这种激烈的优化通常正是编译器在遇到switch语句时所做的事情。但是,只有在所有情况都是相邻整数的情况下才能进行此优化。如果它们不相邻,则可以通过const查找表创建邻接。
但如果案例不是整数类型,则无法完成上述优化。如果它们是浮点数,字符串或其他内容,则没有合理的方法来优化代码。
至少在我的书中,switch
仅出于此目的而存在:它使编译器更容易创建比if-else更有效的代码。因此它与关键字inline,register等属于同一类别,这也使编译器更容易优化代码。
答案 3 :(得分:1)
浮点值通常不能直接比较
x = 1 / 3.0;
switch (x) {
case 0.3333: /* ... */; break;
case 0.333333333875634875634: /* ... */; break;
case 0.333333333784532452321: /* ... */; break;
case 0.333333333847632874632: /* ... */; break;
default: break;
}
与字符串相同(无strcpy(buff, "foobar"); if (buff == "foobar") /* ... */;
)
答案 4 :(得分:0)
好吧,由于舍入错误,浮点值的比较不可靠,默认情况下C不支持字符串比较(仅限函数strcmp,例如。)
- >没有办法由编译器自动确定比较方法。
答案 5 :(得分:0)
简答题是整数类型易于比较,而且比较非常快。 C中的浮点类型无法可靠地进行比较。我不认为C有String类型,但字符串比较慢......你将不得不比较字符数组......慢。我相信有人会给你一个更完整,更科学的答案。
答案 6 :(得分:0)
您必须考虑“如何将此C代码转换为汇编?”。
switch conditionnal只是某种棘手的JMP指令,顺便说一下需要在编译之前对这些情况进行排序(我猜编译器会对你的情况进行排序),但我不太确定。
在php例如,你可以用字符串切换{},这可能会使用某种二分法搜索(它首先查找第一个字符等),就像地图一样。
你必须明白编译语言是产生“相当不错”的汇编代码的一种方式,但这并不意味着它比你刚刚在汇编中完成你的程序更好。
使用像C或C ++这样的语言,你可以快速编写一个程序,你的编译器也可以做一些简单的优化,但是为了上帝的缘故,在使用编程语言时要保持温和,考虑一下你的程序的大图,以及不要忘记语言只是工具,它们并不神奇:如果你忘记了基本的低级行为,你就搞砸了。
答案 7 :(得分:0)
当存在离散数量的选项时,开关可能是最佳选择。编译器可以警告您重复的情况,如果您使用枚举,那么好的编译器会警告未处理的值。
作为一个好的做法,浮动/双打不应该测试相等,“if(f = 3.141516)”是一个令人头疼的邀请,“const float kEpsilon = 1e-5;”然后使用“if(fabs(f - 3.141516)< kEpsilon)” 选择与您的问题相关的epsilon值。内联函数或宏可能有助于以更易读的方式编写它。
答案 8 :(得分:0)
我们不能在开关盒中使用float。这是因为花车不精确。你永远不知道这个数字究竟是什么。