La mejor manera de encender una cuerda en C

En C hay una construcción de switch , que permite ejecutar diferentes twigs condicionales de código basadas en un valor entero de prueba, por ejemplo,

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

¿Cómo es posible obtener el mismo comportamiento (es decir, evitar la llamada ” ifelse ladder”) para un valor de cadena, es decir, un char * ?

Si te refieres a cómo escribir algo similar a esto:

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

Entonces la solución canónica en C es usar una escalera if-else:

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

Si tiene muchos casos y no desea escribir strcmp() llamadas strcmp() , podría hacer algo como:

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

Solo tienes que asegurarte de que tu función hash no tenga colisiones dentro del conjunto de valores posibles para la cadena.

No hay forma de hacer esto en C. Hay muchos enfoques diferentes. Por lo general, lo más simple es definir un conjunto de constantes que representan sus cadenas y hacer una búsqueda por cadena para obtener la constante:

 #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*sizeof(t_symstruct); 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 */ } 

Hay, por supuesto, formas más eficientes de hacer esto. Si mantiene las claves ordenadas, puede usar una búsqueda binaria. También puedes usar una tabla hash. Estas cosas cambian su desempeño a expensas del mantenimiento.

Creo que la mejor manera de hacerlo es separar el ‘reconocimiento’ de la funcionalidad:

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

Mi método preferido para hacer esto es a través de una función hash (prestada desde aquí ). Esto le permite utilizar la eficiencia de una instrucción switch incluso cuando se trabaja con char *:

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

Por supuesto, este enfoque requiere que los valores hash para todos los caracteres aceptados posibles se calculen de antemano. No creo que esto sea demasiado problema; sin embargo, dado que la instrucción switch funciona en valores fijos independientemente. Se puede hacer un progtwig simple para pasar char * a través de la función hash y dar salida a sus resultados. Estos resultados se pueden definir a través de macros como lo he hecho anteriormente.

Hay una forma de realizar la búsqueda de cadenas más rápido. Suposiciones: dado que estamos hablando de una instrucción switch, puedo suponer que los valores no cambiarán durante el tiempo de ejecución.

La idea es usar el qsort y bsearch de C stdlib.

Estaré trabajando en el código de xtofl.

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

Para agregar a la respuesta anterior de Phimueme, si su cadena tiene siempre dos caracteres, entonces puede construir un int entero de 16 bits de los dos caracteres de 8 bits, y encender eso (para evitar declaraciones de cambio / caso nesteds).

Por lo general, así es como lo hago.

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

He publicado un archivo de cabecera para realizar el cambio de las cadenas en C. Contiene un conjunto de macro que oculta la llamada a strcmp () (o similar) para imitar un comportamiento similar a un conmutador. Lo he probado solo con GCC en Linux, pero estoy bastante seguro de que se puede adaptar para soportar otro entorno.

EDITAR: agregó el código aquí, según lo solicitado

Este es el archivo de encabezado que debe incluir:

 #ifndef __SWITCHS_H__ #define __SWITCHS_H__ #include  #include  #include  /** Begin a switch for the string x */ #define switchs(x) \ { char *__sw = (x); bool __done = false; bool __cont = false; \ regex_t __regex; regcomp(&__regex, ".*", 0); do { /** Check if the string matches the cases argument (case sensitive) */ #define cases(x) } if ( __cont || !strcmp ( __sw, x ) ) \ { __done = true; __cont = true; /** Check if the string matches the icases argument (case insensitive) */ #define icases(x) } if ( __cont || !strcasecmp ( __sw, x ) ) { \ __done = true; __cont = true; /** Check if the string matches the specified regular expression using regcomp(3) */ #define cases_re(x,flags) } regfree ( &__regex ); if ( __cont || ( \ 0 == regcomp ( &__regex, x, flags ) && \ 0 == regexec ( &__regex, __sw, 0, NULL, 0 ) ) ) { \ __done = true; __cont = true; /** Default behaviour */ #define defaults } if ( !__done || __cont ) { /** Close the switchs */ #define switchs_end } while ( 0 ); regfree(&__regex); } #endif // __SWITCHS_H__ 

Y así es como lo usas:

 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"); cases("2") printf("2\n"); break; defaults printf("No match\n"); break; } switchs_end; 

Así es como lo haces. No en realidad no.

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

Si se trata de una cadena de 2 bytes, puede hacer algo como en este ejemplo concreto en el que enciendo los códigos de idioma ISO639-2.

  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_ * son enteros constantes utilizados para indexar en matrices.

Asumiendo poca endianness y sizeof (char) == 1, podrías hacer eso (algo como esto fue sugerido por MikeBrom).

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

Podría ser generalizado para el caso de BE.

Los punteros de función son una excelente manera de hacerlo, p. Ej.

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

… esto llama a una función que ha establecido por la clave de cadena (una función por caso):

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

Utilice una implementación preexistente de hashmap / tabla / diccionario como khash, devuelva ese puntero a una función dentro de switchFunction() , y ejecútelo (o simplemente devuélvalo desde switchFunction() y ejecútelo usted mismo). Si la implementación del mapa no almacena eso, simplemente use un uint64_t en uint64_t lugar, y uint64_t según un puntero.

No podemos escapar de la escalera if-else para comparar una cadena con otras. Incluso la caja de conmutadores regular también es una escalera if-else (para enteros) internamente. Es posible que solo deseemos simular la caja del conmutador para cadena, pero nunca podemos reemplazar la escalera if-else. Lo mejor de los algoritmos para comparación de cadenas no puede escapar del uso de la función strcmp. Significa comparar carácter por personaje hasta que se encuentre una falta de coincidencia. Entonces usar if-else ladder y strcmp son inevitables.

MANIFESTACIÓN

Y aquí están las macros más sencillas para simular el cambio de mayúsculas y minúsculas.

 #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 

Y puedes usarlos como

 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 

Salida:

 in def in ghi in DEFAULT 

Debajo está el uso de SWITCH nested:

 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 

Salida:

 in def in def => xyz in ghi in DEFAULT 

Aquí está SWITCH de cadena inversa, donde puedes usar una variable (en lugar de una constante) en la cláusula CASE:

 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 

Salida:

 in str3 

Hola, esta es la manera fácil y rápida si tienes este caso:

[Modo rápido]

 int concated; char ABC[4]="";int a=1,b=4,c=2; //char[] Initializing ABC<-sprintf(ABC,"%d%d%d",a,b,c); //without space between %d%d%d printf("%s",ABC); //value as char[] is =142 concated=atoi(ABC); //result is 142 as int, not 1,4,2 (separeted) //now use switch case on 142 as an integer and all possible cases 

[Modo EXPLICADO]

por ejemplo: tengo muchos menús, cada elección en el primer menú lo lleva al segundo menú, lo mismo con el segundo menú y el tercer menú. Pero las opciones son diferentes para que sepa que el usuario ha finalizado definitivamente. ejemplo:

menú 1: 1 ==> menú 2: 4 ==> menú 3: 2 (...) la opción es 142. otros casos: 111,141,131,122 ...

solución: almacene la primera 1ra en a, 2da en b, 3ra en c. a = 1, b = 4, c = 2

  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); //result is 142 as int, not 1,4,2 (separeted)