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

16

109

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 *?

Loper answered 25/10, 2010 at 13:10 Comment(4)
What do you mean by "switch on"?Archimage
Please reformulate so that the question makes clear what you are actually trying to do (or asking).Flo
The OP probably wants to use a string as the paramater of a switch statement. As far as I know, this is not possible.Dipstick
You cannot use strings in switch statements, but to avoid checking one string at a time with a naive else if ladder, I suggest a binary searchEncipher
C
143

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: */
{
}
Cauthen answered 25/10, 2010 at 13:18 Comment(8)
Actually, the problem is that I already have a switch on int and in a special case I have the value "B1" and "B2" which I want to use in same switch. The Only way is to somehow convert the "B1" and "B2" values and use them as int!!?Loper
@Niklas: This is important information for your question. Can you update your question and explain (if possible with some (pseudo-)code) what you are trying to do?Cauthen
@Niklas: You should clarify your question: how on earth could "B1" and "B2" be a special case of an int?Vivianna
@Niklas: if you need to make decisions based on different data types, then you will have to change your control structure. The easiest and most straightforward is to use an if-else chain. Alternately, instead of switching on a variable, you can switch on a function that will take an int and char * as arguments and return an int value based on those inputs.Unexpressed
Edgar: Maybe he means 0xB1 and 0xB2?Candlestand
Gooosh, this is what I mean. I have these defines (NOOO, I can't change these defines)Loper
#define A 1 #define B 2 #define C S1 #define D S2 and these values are what I want to use in my switch. So simple :-)Loper
@Niklas: Defines are not strings. If the define is for a number, you can use it directly in your switch like this switch (something) { case A: /*...*/ break; case B: /*...*/ break; }.Cauthen
V
67

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.

Vivianna answered 25/10, 2010 at 13:24 Comment(10)
"make sure your hash function has no collisions inside the set of possible values for the string." -- Does such a hash function exists for the alphabet [a-zA-Z0-9_]? Any example?Forehanded
@ArunSaha: Obviously not for arbitrary combinations of such characters.Vivianna
If you use fixed length string keys, you can convert them each into unique integers; no collisions possible.Moonmoonbeam
@ArcaneEngineer Um... isn't that the exact problem the question is trying to solve? How, given the string only, would you pick out an integer to go with it? "use a switch or if/else ladder" Or maybe you mean something very short like 4 characters?Commercialism
@Commercialism I meant anything comparable in a quick op, such as 2 64-bit uints whose bits are treated as 8 1-byte ASCII chars. I implemented this a while ago, for key comparisons within a hash table in C. You thus eradicate the need for any hashing or buckets. The problem comes in where you need to exceed 64 bits; you then pay the cost for conditionals as you loop over each set of 8 chars in the full string. Unless you unroll the loop, if you know the max size of keys. It's a fine balancing act.Moonmoonbeam
@ArcaneEngineer That's actually brilliant, should be an answer here... ;-)Commercialism
String switching was introduced as a language feature in Java 7. The compiler performs a code transformation involving a hash function, just like your own example.Lauder
Qt has built-in support for switching strings like this, probably via a hash function under the hoodDiocletian
A brilliant implementation of this, without manually calculating the hash: heeden.nl/statichashc.htmMegrim
@Megrim No hash function that takes an arbitrary C string and returns an integral value can ever be guaranteed to not produce collisions. In fact, such a function must produce collisions. KISS - just use an if-else if ladder.Vino
C
49

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.

Chesterfield answered 25/10, 2010 at 13:27 Comment(4)
Much nicer to use an enum instead of a set of #defines for the keys, but otherwise about the best you can do.Luteous
the incrementing is incorrect. lookuptable + i*sizeof(t_symstruct) is not equal to lookuptable[i].Compartment
@Compartment That's how pointer arithmetic works in c. The sizeof is implicit.Rheumatism
is it faster than hardcoded memcmp() ?Driskell
M
26

My preferred method for doing this is via a hash function (borrowed from here). This allows you to utilize the efficiency of a switch statement even when working with char *'s:

#include "stdio.h"

#define LS 5863588
#define CD 5863276
#define MKDIR 210720772860
#define PWD 193502992

const unsigned long hash(const char *str) {
    unsigned long hash = 5381;  
    int c;

    while ((c = *str++))
        hash = ((hash << 5) + hash) + c;
    return hash;
}

int main(int argc, char *argv[]) {
    char *p_command = argv[1];
    switch(hash(p_command)) {
    case LS:
        printf("Running ls...\n");
        break;
    case CD:
        printf("Running cd...\n");
        break;
    case MKDIR:
        printf("Running mkdir...\n");
        break;
    case PWD:
        printf("Running pwd...\n");
        break;
    default:
        printf("[ERROR] '%s' is not a valid command.\n", p_command);
    }
}

Of course, this approach requires that the hash values for all possible accepted char *'s are calculated in advance. I don't think this is too much of an issue; however, since the switch statement operates on fixed values regardless. A simple program can be made to pass char *'s through the hash function and output their results. These results can then be defined via macros as I have done above.

Murat answered 9/5, 2016 at 16:40 Comment(3)
Welcome to Stack Overflow. What you've shown is nicely presented and a good idea, but … but it isn't all that distinctively different from some of the other answers — there are several that use minor variants on this idea. If you add a new answer to an old stable question, you should be very sure you have good new information. That's mostly a word of caution; I'm certainly not about to down-vote you for this.Busman
@JonathanLeffler I think this is the best answer. There is nothing wrong with posting a complete solution. All the other solutions should have been deleted given this one is the best. The if-else chain solution (currently with the best rank) does not address the intent of the original question. It has a high score just because it was posted earlier. If those who voted early compared this one and that one, they would rank this one higher. Stackoverflow should have a way to depreciate older votes. Otherwise, it would cause this weird situation in which the best solution is not ranked the top.Babbling
This allows you to utilize the efficiency of a switch statement even when working with char *'s There's not much if any efficiency gained over and if-else if... ladder of strcmp() calls unless the strings being compared contain relatively long sequences of common initial characters. If the strings all differ within the first 4-8 characters, a properly optimized strcmp() will compare the strings with a single operation, whereas with a hash function the entire string must be hashed every time.Vino
R
18

I think the best way to do this is separate the 'recognition' from the functionality:

struct stringcase { char* string; void (*func)(void); };

void funcB1();
void funcAzA();

stringcase cases [] = 
{ { "B1", funcB1 }
, { "AzA", funcAzA }
};

void myswitch( char* token ) {
  for( stringcases* pCase = cases
     ; pCase != cases + sizeof( cases ) / sizeof( cases[0] )
     ; pCase++ )
  {
    if( 0 == strcmp( pCase->string, token ) ) {
       (*pCase->func)();
       break;
    }
  }

}
Rhoades answered 25/10, 2010 at 13:27 Comment(0)
L
16

I have published a header file to perform the switch on the strings in C. It contains a set of macro that hide the call to the strcmp() (or similar) in order to mimic a switch-like behaviour. I have tested it only with GCC in Linux, but I'm quite sure that it can be adapted to support other environment.

EDIT: added the code here, as requested

This is the header file you should include:

#ifndef __SWITCHS_H__
#define __SWITCHS_H__

#include <string.h>
#include <regex.h>
#include <stdbool.h>

/** Begin a switch for the string x */
#define switchs(x) \
    { char *ss__sw = (x); bool ss__done = false; bool ss__cont = false; \
        regex_t ss__regex; regcomp(&ss__regex, ".*", 0); do {

/** Check if the string matches the cases argument (case sensitive) */
#define cases(x)    } if ( ss__cont || !strcmp ( ss__sw, x ) ) \
                        { ss__done = true; ss__cont = true;

/** Check if the string matches the icases argument (case insensitive) */
#define icases(x)    } if ( ss__cont || !strcasecmp ( ss__sw, x ) ) { \
                        ss__done = true; ss__cont = true;

/** Check if the string matches the specified regular expression using regcomp(3) */
#define cases_re(x,flags) } regfree ( &ss__regex ); if ( ss__cont || ( \
                              0 == regcomp ( &ss__regex, x, flags ) && \
                              0 == regexec ( &ss__regex, ss__sw, 0, NULL, 0 ) ) ) { \
                                ss__done = true; ss__cont = true;

/** Default behaviour */
#define defaults    } if ( !ss__done || ss__cont ) {

/** Close the switchs */
#define switchs_end } while ( 0 ); regfree(&ss__regex); }

#endif // __SWITCHS_H__

And this is how you use it:

switchs(argv[1]) {
    cases("foo")
    cases("bar")
        printf("foo or bar (case sensitive)\n");
        break;

    icases("pi")
        printf("pi or Pi or pI or PI (case insensitive)\n");
        break;

    cases_re("^D.*",0)
        printf("Something that start with D (case sensitive)\n");
        break;

    cases_re("^E.*",REG_ICASE)
        printf("Something that start with E (case insensitive)\n");
        break;

    cases("1")
        printf("1\n");
        // break omitted on purpose

    cases("2")
        printf("2 (or 1)\n");
        break;

    defaults
        printf("No match\n");
        break;
} switchs_end;

Please notice that there is no colon (':') after each case nor after the defaults.

Lobe answered 13/4, 2015 at 12:19 Comment(7)
I edited the example not by adding a "break", but highlighting the fact that you can omit itLobe
that is nicer! Before I use "sscanf" to match, learnt "regex.h", which is super to do with string cases :)Alcine
What a beautiful solution, good readable and much more functionality compared to a switch/case - Thanks! Don't forget the "switchs_end:" after closing bracket.Joliejoliet
Note, though, that identifiers that begin with a double underscore, such as __sw, are reserved identifiers: "All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use."Vino
@AndrewHenle thanks for the info. I update the code adding the prefix "ss" (switch string)Lobe
Is there a reason why the : is missing after each case? Is it because it is not possible to implement? It might be worth drawing attention to that detail in your answer (as it is a difference in syntax from the usual switch/case syntax).Yenyenisei
@Yenyenisei I think it would be possible to implement it, but I should either use some nested switch or auto-generated labels and I think that it would make the header code more messy. I added a line at the end of the answer as you suggested. ThanksLobe
L
7

There is a way to perform the string search faster. Assumptions: since we are talking about a switch statement, I can assume that the values won't be changing during runtime.

The idea is to use the C stdlib's qsort and bsearch.

I'll be working on xtofl's code.

struct stringcase { char* string; void (*func)(void); };

void funcB1();
void funcAzA();

struct stringcase cases [] = 
{ { "B1", funcB1 }
, { "AzA", funcAzA }
};

struct stringcase work_cases* = NULL;
int work_cases_cnt = 0;

// prepare the data for searching
void prepare() {
  // allocate the work_cases and copy cases values from it to work_cases
  qsort( cases, i, sizeof( struct stringcase ), stringcase_cmp );
}

// comparator function
int stringcase_cmp( const void *p1, const void *p2 )
{
  return strcasecmp( ((struct stringcase*)p1)->string, ((struct stringcase*)p2)->string);
}

// perform the switching
void myswitch( char* token ) {
  struct stringcase val;
  val.string=token;
  void* strptr = bsearch( &val, work_cases, work_cases_cnt, sizeof( struct stringcase), stringcase_cmp );
  if (strptr) {
    struct stringcase* foundVal = (struct stringcase*)strptr;
    (*foundVal->func)();
    return OK;
  }
  return NOT_FOUND;
}
Lobbyism answered 20/5, 2013 at 10:21 Comment(0)
G
6

To add to Phimueme's answer above, if your string is always two characters, then you can build a 16-bit int out of the two 8-bit characters - and switch on that (to avoid nested switch/case statements).

Gastronome answered 25/10, 2010 at 13:21 Comment(2)
If you really wish to To add to Phimueme's answer above, then feel free to use the comment function. :)Novia
@Onion: You'll note that MikeBrom does not currently have the reputation to comment on posts other than his own and answers to his own questions. That said, @Mike "above" is slippery in SO, becuase there is no reliable sort-order. Better to link to the answer like "... in Phimueme's answer ..." (though that answer is deleted now, and the link is only good for user with 10k+ reputation).Ensnare
C
4

We cannot escape if-else ladder in order to compare a string with others. Even regular switch-case is also an if-else ladder (for integers) internally. We might only want to simulate the switch-case for string, but can never replace if-else ladder. The best of the algorithms for string comparison cannot escape from using strcmp function. Means to compare character by character until an unmatch is found. So using if-else ladder and strcmp are inevitable.

DEMO

And here are simplest macros to simulate the switch-case for strings.

#ifndef SWITCH_CASE_INIT
#define SWITCH_CASE_INIT
    #define SWITCH(X)   for (char* __switch_p__ = X, int __switch_next__=1 ; __switch_p__ ; __switch_p__=0, __switch_next__=1) { {
    #define CASE(X)         } if (!__switch_next__ || !(__switch_next__ = strcmp(__switch_p__, X))) {
    #define DEFAULT         } {
    #define END         }}
#endif

And you can use them as

char* str = "def";

SWITCH (str)
    CASE ("abc")
        printf ("in abc\n");
        break;
    CASE ("def")              // Notice: 'break;' statement missing so the control rolls through subsequent CASE's until DEFAULT 
        printf("in def\n");
    CASE ("ghi")
        printf ("in ghi\n");
    DEFAULT
        printf("in DEFAULT\n");
END

Output:

in def
in ghi
in DEFAULT

Below is nested SWITCH usage:

char* str = "def";
char* str1 = "xyz";

SWITCH (str)
    CASE ("abc")
        printf ("in abc\n");
        break;
    CASE ("def")                                
        printf("in def\n");
        SWITCH (str1)                           // <== Notice: Nested SWITCH
            CASE ("uvw")
                printf("in def => uvw\n");
                break;
            CASE ("xyz")
                printf("in def => xyz\n");
                break;
            DEFAULT
                printf("in def => DEFAULT\n");
        END
    CASE ("ghi")
        printf ("in ghi\n");
    DEFAULT
        printf("in DEFAULT\n");
END

Output:

in def
in def => xyz
in ghi
in DEFAULT

Here is reverse string SWITCH, where in you can use a variable (rather than a constant) in CASE clause:

char* str2 = "def";
char* str3 = "ghi";

SWITCH ("ghi")                      // <== Notice: Use of variables and reverse string SWITCH.
    CASE (str1)
        printf ("in str1\n");
        break;
    CASE (str2)                     
        printf ("in str2\n");
        break;
    CASE (str3)                     
        printf ("in str3\n");
        break;
    DEFAULT
        printf("in DEFAULT\n");
END

Output:

in str3
Cordellcorder answered 13/8, 2018 at 6:44 Comment(2)
"Even regular switch-case is also an if-else ladder (for integers) internally" that's not true. If possible, the compiler will generate a jump table, which will be much more efficient. See https://mcmap.net/q/23367/-how-switch-case-statement-implemented-or-works-internallyShattuck
@Shattuck Well, the compiler generates a jump table for even for if-else ladder, at least when optimization is enabled.Zoi
Q
2

If it is a 2 byte string you can do something like in this concrete example where I switch on ISO639-2 language codes.

    LANIDX_TYPE LanCodeToIdx(const char* Lan)
    {
      if(Lan)
        switch(Lan[0]) {
          case 'A':   switch(Lan[1]) {
                        case 'N': return LANIDX_AN;
                        case 'R': return LANIDX_AR;
                      }
                      break;
          case 'B':   switch(Lan[1]) {
                        case 'E': return LANIDX_BE;
                        case 'G': return LANIDX_BG;
                        case 'N': return LANIDX_BN;
                        case 'R': return LANIDX_BR;
                        case 'S': return LANIDX_BS;
                      }
                      break;
          case 'C':   switch(Lan[1]) {
                        case 'A': return LANIDX_CA;
                        case 'C': return LANIDX_CO;
                        case 'S': return LANIDX_CS;
                        case 'Y': return LANIDX_CY;
                      }
                      break;
          case 'D':   switch(Lan[1]) {
                        case 'A': return LANIDX_DA;
                        case 'E': return LANIDX_DE;
                      }
                      break;
          case 'E':   switch(Lan[1]) {
                        case 'L': return LANIDX_EL;
                        case 'N': return LANIDX_EN;
                        case 'O': return LANIDX_EO;
                        case 'S': return LANIDX_ES;
                        case 'T': return LANIDX_ET;
                        case 'U': return LANIDX_EU;
                      }
                      break;
          case 'F':   switch(Lan[1]) {
                        case 'A': return LANIDX_FA;
                        case 'I': return LANIDX_FI;
                        case 'O': return LANIDX_FO;
                        case 'R': return LANIDX_FR;
                        case 'Y': return LANIDX_FY;
                      }
                      break;
          case 'G':   switch(Lan[1]) {
                        case 'A': return LANIDX_GA;
                        case 'D': return LANIDX_GD;
                        case 'L': return LANIDX_GL;
                        case 'V': return LANIDX_GV;
                      }
                      break;
          case 'H':   switch(Lan[1]) {
                        case 'E': return LANIDX_HE;
                        case 'I': return LANIDX_HI;
                        case 'R': return LANIDX_HR;
                        case 'U': return LANIDX_HU;
                      }
                      break;
          case 'I':   switch(Lan[1]) {
                        case 'S': return LANIDX_IS;
                        case 'T': return LANIDX_IT;
                      }
                      break;
          case 'J':   switch(Lan[1]) {
                        case 'A': return LANIDX_JA;
                      }
                      break;
          case 'K':   switch(Lan[1]) {
                        case 'O': return LANIDX_KO;
                      }
                      break;
          case 'L':   switch(Lan[1]) {
                        case 'A': return LANIDX_LA;
                        case 'B': return LANIDX_LB;
                        case 'I': return LANIDX_LI;
                        case 'T': return LANIDX_LT;
                        case 'V': return LANIDX_LV;
                      }
                      break;
          case 'M':   switch(Lan[1]) {
                        case 'K': return LANIDX_MK;
                        case 'T': return LANIDX_MT;
                      }
                      break;
          case 'N':   switch(Lan[1]) {
                        case 'L': return LANIDX_NL;
                        case 'O': return LANIDX_NO;
                      }
                      break;
          case 'O':   switch(Lan[1]) {
                        case 'C': return LANIDX_OC;
                      }
                      break;
          case 'P':   switch(Lan[1]) {
                        case 'L': return LANIDX_PL;
                        case 'T': return LANIDX_PT;
                      }
                      break;
          case 'R':   switch(Lan[1]) {
                        case 'M': return LANIDX_RM;
                        case 'O': return LANIDX_RO;
                        case 'U': return LANIDX_RU;
                      }
                      break;
          case 'S':   switch(Lan[1]) {
                        case 'C': return LANIDX_SC;
                        case 'K': return LANIDX_SK;
                        case 'L': return LANIDX_SL;
                        case 'Q': return LANIDX_SQ;
                        case 'R': return LANIDX_SR;
                        case 'V': return LANIDX_SV;
                        case 'W': return LANIDX_SW;
                      }
                      break;
          case 'T':   switch(Lan[1]) {
                        case 'R': return LANIDX_TR;
                      }
                      break;
          case 'U':   switch(Lan[1]) {
                        case 'K': return LANIDX_UK;
                        case 'N': return LANIDX_UN;
                      }
                      break;
          case 'W':   switch(Lan[1]) {
                        case 'A': return LANIDX_WA;
                      }
                      break;
          case 'Z':   switch(Lan[1]) {
                        case 'H': return LANIDX_ZH;
                      }
                      break;
        }
      return LANIDX_UNDEFINED;
    }

LANIDX_* being constant integers used to index in arrays.

Quintinquintina answered 28/8, 2012 at 21:58 Comment(0)
C
2

This is generally how I do it.

void order_plane(const char *p)
{
    switch ((*p) * 256 + *(p+1))
    {
        case 0x4231 : /* B1 */
        {
           printf("Yes, order this bomber.  It's a blast.\n");
           break;
        }

        case 0x5354 : /* ST */
        {
            printf("Nah.  I just can't see this one.\n");
            break;
        }

        default :
        {
            printf("Not today.  Can I interest you in a crate of SAMs?\n";
        }
    }
}
Cheston answered 28/8, 2012 at 22:7 Comment(4)
Interesting. Lacks (probably by choice) defensive coding. And I admire the additional braces in case. Makes the code so much more readable (though I prefer egyptian braces for case).Lobbyism
BTW, you can use constant expressions in case labels. case 'B'<<8+'1': would make this clearer, I think, than 0x4231.Sanalda
I would use a macro. #define twochar(a) (((uint16_t)a[1]<<8)|a[0])Overscore
Code has UB with order_plane(""), yet is the begining of a good idea.Deadly
E
1

This is how you do it. No, not really.

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdint.h>


 #define p_ntohl(u) ({const uint32_t Q=0xFF000000;       \
                     uint32_t S=(uint32_t)(u);           \
                   (*(uint8_t*)&Q)?S:                    \
                   ( (S<<24)|                            \
                     ((S<<8)&0x00FF0000)|                \
                     ((S>>8)&0x0000FF00)|                \
                     ((S>>24)&0xFF) );  })

main (void)
{
    uint32_t s[0x40]; 
    assert((unsigned char)1 == (unsigned char)(257));
    memset(s, 0, sizeof(s));
    fgets((char*)s, sizeof(s), stdin);

    switch (p_ntohl(s[0])) {
        case 'open':
        case 'read':
        case 'seek':
            puts("ok");
            break;
        case 'rm\n\0':
            puts("not authorized");
            break;
        default:
            puts("unrecognized command");  
    }
    return 0;
}

Macro relies on a compiler extension else you may receive

warning: ISO C forbids braced-groups within expressions

'open' is a multi-character character constant and its value is implementation dependent.

Eraser answered 25/10, 2010 at 13:10 Comment(4)
I don't think this is standard C.Deniable
Making the macro support mixed endianness, or a function is left as an exercise for the reader.Cleodell
It is standard C, but not portable. The byte order of the multi-byte char is 'implementation dependend' and doesn't need to reflect the machines byte order. I used that once and got burnt: on Solaris SPARC (big endian) GNU-C 3.4 uses a different byte order than Sunstudio 12.Stunk
@tristopia You are right of course(as right as one can be after attempting to do something like this for real). This is why we should be all using B instead.Cleodell
T
1

Assuming little endianness and sizeof(char) == 1, you could do that (something like this was suggested by this answer).

char* txt = "B1";
int tst = *(int*)txt;
if ((tst & 0x00FFFFFF) == '1B')
    printf("B1!\n");

It could be generalized for BE case.

Tiana answered 25/10, 2010 at 13:29 Comment(5)
Dont do that! This may cause a "data alignment" exception. It's not guaranteed that the char* txt points to an address, that matches alignment requirements of int.Ceresin
@R he asked for that. @Ceresin it's not the case for x86.Tiana
Niklas didn't ask for x86. And since you mentioned the big endian case, you don't address exclusively the x86 environment. So that'Ceresin
Furthermore, multi-byte chars are not necesserly in machine byte order. See my comment to jbcreix answer.Stunk
sizeof(char) == 1 is a guaranteed, safe assumption, but little endianness is not necessarily.Encipher
M
0

Function pointers are a great way to do this, e.g.

result = switchFunction(someStringKey); //result is an optional return value

...this calls a function that you have set by string key (one function per case):

setSwitchFunction("foo", fooFunc);
setSwitchFunction("bar", barFunc);

Use a pre-existing hashmap/table/dictionary implementation such as khash, return that pointer to a function inside of switchFunction(), and execute it (or just return it from switchFunction() and execute it yourself). If the map implementation doesn't store that, just use a uint64_t instead that you cast accordingly to a pointer.

Moonmoonbeam answered 2/2, 2018 at 19:50 Comment(0)
E
-1

Comparing using an if () else if () chain is a linear search, offering O(n) time complexity. For a large number of strings, depending on the use case, one option is using bsearch() (binary search) to achieve O(log n) time complexity.

Below is an example of how bsearch can be used to perform a given action based on an input string similar to a switch statement. This example finds a given mathematical function by name.

static int c(const void *const a, const void *const b) {
    return strcmp(*(const char *const *)a, *(const char *const *)b);
}
static double (*func(const char *const str))(double) {
    static const char *const s[] = {"abs", "acos", "acosh", "asin", "asinh", "atan", "atanh", "cbrt", "ceil", "cos", "cosh", "erf", "erfc", "exp", "expb", "floor", "gamma", "lgamma", "lb", "ln", "log", "round", "sin", "sinh", "sqrt", "tan", "tanh", "trunc"};
    static double (*const f[])(double) = {fabs, acos, acosh, asin, asinh, atan, atanh, cbrt, ceil, cos, cosh, erf, erfc, exp, exp2, floor, tgamma, lgamma, log2, log, log10, round, sin, sinh, sqrt, tan, tanh, trunc};
    const char *const *const r = bsearch(&str, s, sizeof(s)/sizeof(*s), sizeof(*s), c);
    return r ? f[r-s] : NULL;
}
Encipher answered 20/9, 2022 at 19:7 Comment(2)
Binary search has better time complexity, but that doesn't mean that it will be faster. For large arrays binary search will outperform linear search, but for small arrays linear search can outperform binary search. I would be surprised if binary search beat linear search over the number of elements needed to replace a hand-written if-else chain, and I would also be surprised if binary search beat linear search for your use case of 17 function names.Unblock
@user16217248, Use of restrict is wrong for a bsearch() compare function. Pointers a, b, may have the same value - which violates restrict. Best to use c(const void *a, const void *b) given void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));.Deadly
C
-2

This is the easy and fast way if you have this case:

int concated;
char ABC[4] = ""; // char[] Initializing
int a = 1, b = 4, c = 2;
ABC<-sprintf(ABC, "%d%d%d", a, b, c); // No space between %d%d%d
printf("%s", ABC); // Value as char[] = 142
concated = atoi(ABC); // Result is 142 as int, not 1, 4, 2 (separated)

// Now use switch case on 142 as an integer and all possible cases

Explanation:
For example, if I have many menus, each choice on the 1st menu takes you to the 2nd menu, the 2nd to the 3rd menu, and so on. But the Options are different.
You know that the user has chorused finally. Example:

Menu 1: 1 -> Menu 2: 4 -> Menu 3: 2

The choice is 142. Other cases: 111, 141, 131, 122 ...

Solution:
Store the first 1st in A, 2nd in B, 3rd in C.

char ABC[4] = "";
ABC<-sprintf(ABC, "%d%d%d", a, b, c); // Without space between %d%d%d
printf("%s", ABC); // Value as char[] = 142

// Now you want to recover your value (142) from char[] to int as int value 142

concated = atoi(ABC);
Cholecystotomy answered 13/6, 2017 at 11:2 Comment(1)
I think in your case, using nested switch would be more clear.Zoi

© 2022 - 2024 — McMap. All rights reserved.