11

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.

ipkiss
  • 13,311
  • 33
  • 88
  • 123
  • IMHO, I suppose that it is because of the fixed size and constant values of integer. You can use char too, when expressed as int : http://stackoverflow.com/questions/650162/why-switch-statement-cannot-be-applied-on-strings – vaugham Mar 03 '11 at 11:02
  • 2
    Dude, just FYI, floats are not easily comparable (think about precision to the right side of the decimal point.) Think about it. – luis.espinal Mar 03 '11 at 11:10

9 Answers9

18

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.

unwind
  • 391,730
  • 64
  • 469
  • 606
  • 1
    For sparse set of values, some compilers are also using binary search; some should even be able to start with a binary search and then switch to a jump table for a dense region. Something else I've seen described is to use profile feed back to skew the binary search in order to favorize common cases. – AProgrammer Mar 03 '11 at 11:51
  • However if there are only a handfull of switch cases a simple linear if(x == case1) { ... }; if (x == case1) { ... }; ... may take less instructions than a full blown binary search. It always depends on the actual problem. – datenwolf Mar 03 '11 at 12:20
3

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.

log0
  • 10,489
  • 4
  • 28
  • 62
3

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.

Lundin
  • 195,001
  • 40
  • 254
  • 396
1

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") /* ... */;)

pmg
  • 106,608
  • 13
  • 126
  • 198
0

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

Dov Benyomin Sohacheski
  • 7,133
  • 7
  • 38
  • 64
CheTan Neve
  • 67
  • 1
  • 10
0

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.

Tobias Schittkowski
  • 2,221
  • 2
  • 16
  • 25
0

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.

m0s
  • 4,250
  • 9
  • 41
  • 64
0

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.

jokoon
  • 6,207
  • 11
  • 48
  • 85
  • No, no, `switch` cases will not be sorted in general, for the simple reason that they may overlap if they don't end with a break. More generally, `case`-labels are just labels in an arbitrarily nested block of statements. – Jens Gustedt Mar 03 '11 at 14:03
0

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.

gatoAlfa
  • 120
  • 4