2016-07-27 24 views
5

Czy można przepisać dalej, więc muszę zmienić tylko w jednym miejscu, jeśli zmienia się ciąg?W jaki sposób mogę automatycznie uzyskać pierwszy znak stałego łańcucha jako stałą postać?

#define MY_STRING "Foo bar" 
#define MY_STRING_FIRST_CHAR 'F' 

Poniżej jest nie do przyjęcia, ponieważ odnosi się do char w miejscu pamięci, więc nie może być stosowany jako przypadek w switch stwierdzeniem:

#define MY_STRING_FIRST_CHAR MY_STRING[0] 

switch (something) { 
    case MY_STRING_FIRST_CHAR: 
     break; 
} 

Celem jest efektywne przeanalizuj otrzymany ciąg, patrząc na jeden znak. W moim przypadku wszystkie ciągi mają jedną unikalną postać. Poniższa nie jest mój rzeczywisty kod, ale bardzo prosty przykład, aby pokazać zasadę:

#define COMMAND_LIST    "list" 
#define COMMAND_LIST_FIRST_CHAR 'l' 
#define COMMAND_CHANGE   "change" 
#define COMMAND_CHANGE_FIRST_CHAR 'c' 
#define COMMAND_EXIT    "exit" 
#define COMMAND_EXIT_FIRST_CHAR 'e' 

switch(received_command_string[0]){ 
    case COMMAND_LIST_FIRST_CHAR: 
    // Do the "list" stuff 
    break; 
    case COMMAND_CHANGE_FIRST_CHAR: 
    // Do the "change" stuff 
    break; 
    case COMMAND_EXIT_FIRST_CHAR: 
    // Do the "exit" stuff 
    break; 
} 

użytkownika „PMG” Znalazłem to w gcc dokumentacji: „Nie ma sposobu, aby przekształcić makro argumentu w charakterze stały."

Chciałem, aby definicje znajdowały się w pliku włączającym, który może być współdzielony przez kilka plików źródłowych. Jest to tak blisko, jak tylko mogę uzyskać, mając tylko jeden znak zdefiniowany w jednym miejscu:

#include <stdio.h> 
#define CH0 'F' 
#define CH1 'o' 
#define CH2 'o' 
#define CH3 ' ' 
#define CH4 'b' 
#define CH5 'a' 
#define CH6 'r' 
static char MY_STRING[] = { CH0, CH1, CH2, CH3, CH4, CH5, CH6, '\0'}; 
#define MY_STRING_FIRST_CHAR CH0 

void main(void){ 
    printf("The string is %s, the first char is %c\n", MY_STRING, MY_STRING_FIRST_CHAR); 
} 

Nie zrobię tego w ten sposób. Pierwotne pytanie brzmiało, czy możliwe jest udostępnienie jednej definicji, aby uzyskać zarówno stałą łańcuchową, jak i stałą znaku. Przez marnowanie cykli zegara w czasie działania istnieje kilka rozwiązań mojego problemu.

+3

ciekawe pytanie. Wątpię, czy to możliwe. Ale pachnie dla mnie problemem XY. –

+1

Jeśli chcesz użyć znaku w etykiecie sprawcy, będziesz potrzebować [wyrażenia w liczbie całkowitej] (http://port70.net/~nsz/c/c99/n1256.html#6.6). – nwellnhof

+0

Możliwy duplikat [sprawy przełączania: błąd: etykieta przypadku nie redukuje się do stałej całkowitej] (http://stackoverflow.com/questions/14069737/switch-case-error-case-label-does-not-reduce-to -an-integer-constant) – jweyrich

Odpowiedz

-4
#define MY_STRING "Hello" 

const char var[] = MY_STRING; 

switch(var[0]) { 
    case 'H': 
    break; 
    case 'A': 
    break; 
} 

To powinno rozwiązać problem.

+3

Czy może być użyty w instrukcji 'switch'? Ludzie, przeczytaj pytanie zanim odpowiesz! –

+1

Przeczytaj uważnie (zaktualizowane) pytanie – Leon

3

Można to zrobić z pisaniem każdego symbolu raz ... ale na różnych definicjach

#include <stdio.h> 

#define COMMAND_LIST_FIRST_CHAR 'l' 
#define COMMAND_LIST    (char[]){ COMMAND_LIST_FIRST_CHAR, 'i', 's', 't', 0 } 

int main(void) { 
    char received_command_string[] = "list"; 
    switch (received_command_string[0]) { 
     case COMMAND_LIST_FIRST_CHAR: 
      printf("Doing the \"list\" stuff for '%s'\n", COMMAND_LIST); 
      break; 
     default: 
      break; 
    } 
    return 0; 
} 
+0

Oooops, zobaczyłem to po dodaniu podobnego, ale brzydszego przykładu do oryginalnego postu. Twoje rozwiązanie jest bardziej eleganckie. – Hans

+2

Moje brzydkie rozwiązanie nie określiło dokładnie długości ciągu, ale wydaje się, że stałe "5" można usunąć również z kodu. – Hans

1

Dlaczego koniecznie chcesz używać przypadek przełącznika?

Zamiast tego można użyć tabeli odwzorowania pasującej do łańcucha i funkcji obsługi. Następnie musisz po prostu powtórzyć analizę nad stołem.

typedef struct { 
    char * key; 
    void (func*)(void); 
} MAP_ENTRY; 

MAP_ENTRY map [] = { 
    {"list", listHandler}, 
    {"change", changeHandler}, 
    {"exit", exitHandler}, 
}; 

for (i = 0; i < sizeof(map)/sizeof(map[0]); i++) { 
    if (map[i].key[0] == received_command_string[0]) { 
     map[i].func(); 
     break; 
    } 
} 

Wtedy po prostu trzeba przesunąć przetwarzania kodu ze swojego przełącznika/sprawy do odpowiedniej funkcji obsługi

+1

Ten kod nie ma pożądanej wydajności w porównaniu z podejściem opartym na przełączaniu, które działałoby w czasie O (1). Niezależnie od tego, czy wydajność tego kodu naprawdę ma tak duże znaczenie, tylko OP może powiedzieć. – Leon

+0

@Leon Nie zgadzam się z Twoim oświadczeniem. Z punktu widzenia wydajności należy uwzględnić optymalizacje kompilatora. Dzięki powyższemu kodowi każdy nowoczesny kompilator skonfigurowany pod kątem optymalizacji wydajności wykonałby rozwijanie pętli, co spowodowałoby, że powstały kod binarny byłby prawie taki sam jak prosty przełącznik/obudowa. Bardzo często błędem jest poświęcenie czytelności kodu, aby dokonać wczesnych optymalizacji, które kompilator mógłby zrobić lepiej niż ty. – greydet

+0

Rozwijanie pętli nie może osiągnąć wydajności wyszukiwania w czasie przez tabelę. Co z czytelnością a wydajnością, zgadzam się z tobą, a mój pierwszy komentarz zawiera o tym wskazówkę. – Leon