How can I compare strings in C using a `switch` statement?

In C there is a switch construct which enables one to execute different conditional branches of code based on an test integer value, e.g.,

int a;
/* Read the value of "a" from some source, e.g. user input */
switch (a) {
  case 100:
    // Code
    break;
  case 200:
    // Code
    break;
  default:
    // Code
    break;
}

How is it possible to obtain the same behavior (i.e. avoid the so-called "if-else ladder") for a string value, i.e., a char *?


Solution 1:

If you mean, how to write something similar to this:

// switch statement
switch (string) {
  case "B1": 
    // do something
    break;
  /* more case "xxx" parts */
}

Then the canonical solution in C is to use an if-else ladder:

if (strcmp(string, "B1") == 0) 
{
  // do something
} 
else if (strcmp(string, "xxx") == 0)
{
  // do something else
}
/* more else if clauses */
else /* default: */
{
}

Solution 2:

If you have many cases and do not want to write a ton of strcmp() calls, you could do something like:

switch(my_hash_function(the_string)) {
    case HASH_B1: ...
    /* ...etc... */
}

You just have to make sure your hash function has no collisions inside the set of possible values for the string.

Solution 3:

There is no way to do this in C. There are a lot of different approaches. Typically the simplest is to define a set of constants that represent your strings and do a look up by string on to get the constant:

#define BADKEY -1
#define A1 1
#define A2 2
#define B1 3
#define B2 4

typedef struct { char *key; int val; } t_symstruct;

static t_symstruct lookuptable[] = {
    { "A1", A1 }, { "A2", A2 }, { "B1", B1 }, { "B2", B2 }
};

#define NKEYS (sizeof(lookuptable)/sizeof(t_symstruct))

int keyfromstring(char *key)
{
    int i;
    for (i=0; i < NKEYS; i++) {
        t_symstruct *sym = lookuptable[i];
        if (strcmp(sym->key, key) == 0)
            return sym->val;
    }
    return BADKEY;
}

/* ... */
switch (keyfromstring(somestring)) {
case A1: /* ... */ break;
case A2: /* ... */ break;
case B1: /* ... */ break;
case B2: /* ... */ break;
case BADKEY: /* handle failed lookup */
}

There are, of course, more efficient ways to do this. If you keep your keys sorted, you can use a binary search. You could use a hashtable too. These things change your performance at the expense of maintenance.