关于 C 中的 switch{} 情况?

发布于 2024-10-20 15:31:06 字数 169 浏览 2 评论 0原文

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

我只是好奇为什么 switch{} case 不接受其他类型,例如 float 或 string。这背后有什么原因吗?

多谢。

I am reading some text in C language. The text says that switch{} case can only accept integer type.

I am just curious about why switch{} case does not accept other types such as float or string. Are there any reasons behind this?

Thanks a lot.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

裂开嘴轻声笑有多痛 2024-10-27 15:31:30

当有多个选项时,开关可能是最佳选择。编译器可以警告您重复的情况,如果您使用枚举,好的编译器会警告您未处理的值。

作为一个好的实践,浮点数/双精度数不应该测试相等性,“if(f = 3.141516)”会让人头疼,“const float kEpsilon = 1e-5;”然后使用“if(fabs(f - 3.141516) < kEpsilon)”
选择与您的问题相关的 epsilon 值。内联函数或宏可能有助于以更具可读性的方式编写此内容。

A switch is probably the best choice when there is a discrete number of options. The compiler can warn you about duplicate cases and if you use enums good compiler will warn about values not handled.

As a good practice floats/doubles should not tested for equality, "if(f = 3.141516)" is an invitation for headaches, "const float kEpsilon = 1e-5;" and then use "if(fabs(f - 3.141516) < kEpsilon)"
Pick an epsilon value that is relevant for your problem. An inline function or macro may help to write this in a more readable way.

浪漫人生路 2024-10-27 15:31:30

我们不能在开关盒中使用浮动。这是因为浮动不精确。你永远不知道这个数字实际上会是多少。

We cannot use float in switch case. It's because floats are imprecise. You never know what that number is actually going to be.

無處可尋 2024-10-27 15:31:29

你必须思考“如何将这段 C 代码转换为汇编语言?”。

switch 条件只是某种棘手的 JMP 指令,顺便说一句,它需要在编译之前对案例进行排序(我猜编译器会对您的案例进行排序),但我不太确定。

例如,在 php 中,您可以使用字符串进行 switch{} 操作,这可能会使用某种二分搜索(它首先查找第一个字符等),就像地图一样。

您必须了解编译语言是生成“相当好的”汇编代码的某种方式,但这并不意味着它会比您仅用汇编语言编写程序更好。

使用像 C 或 C++ 这样的语言,您将能够快速编写程序,并且您的编译器将能够进行琐碎的优化,但看在上帝的份上,使用编程语言时要温和,先考虑一下程序的大局,然后不要忘记语言只是工具,它们并不神奇:如果你忘记了基本的低级行为,你就完蛋了。

You have to think about "how can this C code be converted to assembly ?".

The switch conditionnal is just some sort of tricky JMP instruction, which by the way needs the cases to be sorted before compilation (I guess the compiler will sort your cases), but I'm not so sure.

In php for example you can does switch{} with string though, that may use some kind of dichotomial search (it looks for the first characters first, etc.), just like maps.

You have to understand that compiled languages are some way of generating "pretty good" assembly code, but that doesn't mean it will be better than if you just did your program in assembly.

With a language like C or C++, you will make a program quickly and your compiler will be able to do trivial optimizations, but for god's sake, be gentle when using a programming language, think about your program's big picture before, and don't forget that languages are just tools, they are not magical: if you forget about basic low level behavior, you screwed.

不奢求什么 2024-10-27 15:31:26

简而言之,整数类型很容易比较,而且比较速度非常快。 C 中的浮点类型无法可靠地进行比较。我不认为 C 有 String 类型,但是字符串比较起来很慢...你必须比较字符数组...慢。相信一定会有人给你更完整、更科学的答案。

Short answer is integer types are easy to compare, and the comparison is very fast. Floating point types as they are in C can't be compared reliably. I don't think C has String types, but strings are slow to compare... you will have to compare arrays of characters... slow. I'm sure someone will give you a more complete and more scientific answer.

◇流星雨 2024-10-27 15:31:24

好吧,由于舍入误差,浮点值的比较并不可靠,并且 C 默认情况下不支持字符串比较(仅通过函数 strcmp 例如)

->编译器无法自动确定比较方法。

Well, a comparison of float values is not reliable due to rounding errors, and string comparison is not supported by default by C (only by function strcmp e.g.)

-> there is no way to automatically determine the comparison methods by the compiler.

来日方长 2024-10-27 15:31:18

浮点值通常不能直接比较

x = 1 / 3.0;
switch (x) {
  case 0.3333: /* ... */; break;
  case 0.333333333875634875634: /* ... */; break;
  case 0.333333333784532452321: /* ... */; break;
  case 0.333333333847632874632: /* ... */; break;
  default: break;
}

与字符串相同 (no strcpy(buff, "foobar"); if (buff == "foobar") /* ... */;)

floating point values are not usually directly comparable

x = 1 / 3.0;
switch (x) {
  case 0.3333: /* ... */; break;
  case 0.333333333875634875634: /* ... */; break;
  case 0.333333333784532452321: /* ... */; break;
  case 0.333333333847632874632: /* ... */; break;
  default: break;
}

Same with strings (no strcpy(buff, "foobar"); if (buff == "foobar") /* ... */;)

苍暮颜 2024-10-27 15:31:14

我会回答一个问题:为什么使用 switch 语句而不是 if...else if?

令人惊讶的是,许多程序员从不问这个问题,而是将 switch 视为某些东西语言中必须存在的基础知识。那不是真的!您可以编写任何类型的 C 程序,而无需使用 switch。严格来说,switch 是一个冗余功能。

那么为什么要使用它呢?

可读性并不是原因。 switch 实际上比 if-else 的语法更糟糕、更不直观。 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 和 switch 在语法和功能上是等效的,并且应该编译为完全相同的机器代码。以下是此示例 switch 的统计信息,

         if-else  switch
Symbols    33       65        // Not counting the expressions
Lines      12       19        // Not counting empty lines

需要更多代码才能获得相同结果,因此必须将其视为比 if-else 可读性较差

在你开始争论 switch 看起来更像一个表而不是 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 来提高可读性,但我宁愿将这种偏离主题的讨论留给非编程相关的网站。


所以 switch 的安全性和可读性都比 if-else 差。那么剩下的可能对程序员有吸引力的是效率。具有必须由程序测试的 n 个案例的程序员肯定希望有一些东西可以尽快搜索正确的案例。以线性方式检查所有情况是一个坏主意。

您可能知道,通过将 if-else 或 switch 实现为函数指针数组,可以对 if-else 或 switch 进行大量优化:

typedef void (*Func_t)(void);

const Func_t cases [N] = { ... };
cases[i]();

这种彻底的优化通常正是编译器在遇到 switch 语句时所做的事情。然而,这种优化只有在所有情况都是相邻整数的情况下才能完成。如果它们不相邻,则可以通过 const 查找表创建邻接关系。

但如果case不是整数类型,则无法进行上述优化。如果它们是浮点数、字符串或其他东西,则没有明智的方法来优化代码。

因此,至少在我的书中,switch 仅出于此目的而存在:它使编译器更容易创建比 if-else 更有效的代码。因此,它与关键字 inline、register 等属于同一类别,这也使编译器更容易优化代码。

I would answer with a question: why are you using a switch statement and not if...else if?

Surprisingly, many programmers never ask that question but regard switch for something fundamental that must be there in the language. That is not true! You can write any kind of C program without ever using switch. Strictly speaking, switch is a redundant feature.

So why use it?

Readability is not the reason why. switch actually has far worse and less intuitive syntax than if-else. The need for break statements inside the switch, the weird syntax rules of switch that allows a case to be declared inside a local scope of another case, the arbitrary location of the default.

switch is not only less readable, it is also much more bug-prone than if-else. The forgotten break is the most obvious hazard, that has lead to millions upon millions of hard-to-find bugs in software.

Another more obvious argument against switch being more readable is this "bare bone" code:

if (A)
{
}

else if (B)
{
}

else if (C)
{
}

else
{
}


switch(something)
{
  case A:
  {
    break;
  }

  case B:
  {
    break;
  }

  case C:
  {
    break;
  }

  default:
  {
    break;  
  }
}

The if and the switch above are equivalent in syntax and function, and should be compiled down to the very same machine code. Here is the statistics from this example

         if-else  switch
Symbols    33       65        // Not counting the expressions
Lines      12       19        // Not counting empty lines

switch requires more code for the same result, thus is must be regarded as less readable than if-else.

Before you start to argue about switch looking more like a table than if-else, that is all about code formatting, and irrelevant. Nothing in the standard prevents you from writing code like this:

if      (A) {}
else if (B) {}
else if (C) {}
else        {}

switch(something)
{
  case A:   { break; }
  case B:   { break; }
  case C:   { break; }
  default:  { break; }
}

I would consider either form to be readable, if you wish for some sort of minimal table-like syntax.

There may of course be aesthetic, superstitious or religious reasons why switch should be used for readability, but I rather leave such off-topic discussions to non-programming related sites.


So switch is less safe and less readable than if-else. What then remains that may appeal to a programmer is efficiency. A programmer with n cases that must be tested by the program would surely like to have something that makes the search for the correct case as quick as possible. It is a bad idea to examine all cases in a linear fashion.

As you may know, it is possible to optimize an if-else or switch quite a lot, by implementing it as an array of function pointers:

typedef void (*Func_t)(void);

const Func_t cases [N] = { ... };
cases[i]();

This drastic optimization is often exactly what a compiler does when encountering a switch statement. However, this optimization can only be done if all cases are adjacent integers. If they are not adjacent, then the adjacency can be created through a const lookup table.

But if the cases aren't of integer type, the above optimization cannot be done. If they were floats, strings or something else, there is no sensible way to optimize the code.

So at least in my book, switch only exists for this very purpose: it makes it easier for the compiler to create more effective code than if-else. Thus it falls into the same category as keywords inline, register etc, that also make it easier for the compiler to optimize your code.

宁愿没拥抱 2024-10-27 15:31:12

C的语言哲学是所见即所得。没有隐藏机制。这实际上是该语言的强大优势之一。

切换整数涉及预期的分支,而比较浮点和字符串会产生隐藏成本。

The language philosophy of C is what you see is what you get. There is no hidden mechanisms. This is actually one of the great strength of the language.

Switching on integer involves a branching as expected, while comparing on float and string would have a hidden cost.

风向决定发型 2024-10-27 15:31:09

经典的原因可能是对于整数值的“决策表达式”,可以进行非常好的优化。

基本上,您可以将 case 语句列表映射到包含地址的表,然后根据值直接跳转。显然,对于浮点数和字符串来说这是行不通的。

在 GCC 中,您可以使用 some 手动执行此操作像这样的扩展

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;
}

这通常称为“计算的转到”,并且应该清楚如何将 switch 基本上编译为非常相似的东西。打开表达式的严格定义会有所帮助,例如使用enum

此外,C 在语言级别上并没有太多字符串的概念。

The classical reason is probably that for integer-valued "decision expressions", it's possible to do very nice optimizations.

Basically, you can map the list of case-statements to a table containing addresses, and then directly jump based on the value. Obviously, for floats and strings that doesn't work.

In GCC, you can do this by hand using some extensions like so:

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;
}

This is in general called a "computed goto", and it should be clear how a switch can basically be compiled down to something very similar. Tight definition of the switched-on expression helps, such as using an enum.

Also, C doesn't really have much of a concept of strings at the language level.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文