关于C中的switch {}案例?

时间:2011-03-03 10:59:24

标签: c switch-statement

我正在阅读C language中的一些文字。文中说switch{} case只能接受整数类型。

我很好奇为什么switch{} case不接受其他类型,如float或string。这背后有什么理由吗?

非常感谢。

9 个答案:

答案 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。这是因为花车不精确。你永远不知道这个数字究竟是什么。