Forum: Compiler & IDEs switch-case mit 4-Byte-Strings


von Johannes K. (joee)


Lesenswert?

Hallo,

folgendes Problem:

uint32_t cmd;  //4 Bytes

cmd = LeseCmd(); //4 bytes

switch (cmd)
{
   case "ABCD"   //4 bytes, (ohne \0)
      ....;
}



Ich hätte hier gerne, dass beim kompilieren als Case-Vergleichswert der 
entsprechende ASCII-Code 12345678 benutzt wird, ohne dass ich diesen per 
Hand "ausrechnen" muss.

Für jeden dezenten Denkanstoß bin ich überaus dankbar.

PS:
'A' + ('B'<<8)+ ('C'<<16)+ ('D'<<24) geht, gefällt mir aber nicht.

von Rolle (Gast)


Lesenswert?

Vieles was man gern hätte, muss man einfache selber machen !!!

Soooo geht das nicht !!!

von Rolle (Gast)


Lesenswert?

Nein, jetz mal im Ernst:

Mir ist keine Präprozessor Anweisung bekannt die sowas leistet
und im case-Statement muss halt mal eine Konstante stehen

Aber machs halt so:

#define _ABCD    ('A' + ('B'<<8)+ ('C'<<16)+ ('D'<<24))
#define _ABC1    ('A' + ('B'<<8)+ ('C'<<16)+ ('1'<<24))
#define _ABC2    ('A' + ('B'<<8)+ ('C'<<16)+ ('2'<<24))
#define _ABC3    ('A' + ('B'<<8)+ ('C'<<16)+ ('3'<<24))
.
.
.


uint32_t cmd;  //4 Bytes

cmd = LeseCmd(); //4 bytes

switch (cmd)
{
   case _ABCD:        ....;
   case _ABC1:        ....;
   case _ABC2:        ....;
   case _ABC3:        ....;
      ....;
}

dann sieht dein Programm wenigstens schön aus


Viel Spass noch !!!

von Peter D. (peda)


Lesenswert?

1
#define MK32(a,b,c,d) (((uint32_t)'a')<<24 | ((uint32_t)'b')<<16 | 'c'<<8 | 'd')
2
...
3
case MK32(A,B,C,D):


Peter

von DirkB (Gast)


Lesenswert?

Versuch doch mal
1
int i = 'ABCD'; // einfaches Hochkomma
Der Compiler warnt zwar, aber gemacht hat er es.

von Werner B. (werner-b)


Lesenswert?

Teste doch mal ob dein Compiler
1
  case 'ABCD':

verdaut. Mit einige Compilern funktioniert das. Es muss aber die 
Endianess beachtet werden. Dann muss mglw. mit
1
  case 'DCBA':

verglichen werden.

Edit:
Da war Dirk schneller ;-)

von Rolle (Gast)


Lesenswert?

Peter Dannegger schrieb:
> #define MK32(a,b,c,d) (((uint32_t)'a')<<24 | ((uint32_t)'b')<<16 | 'c'<<8 | 'd')
> ...
> case MK32(A,B,C,D):


Das ist mal schnell schön hingeschrieben, geht aber nicht !!!

von Rolle (Gast)


Lesenswert?

Rolle schrieb:
> Peter Dannegger schrieb:
>> #define MK32(a,b,c,d) (((uint32_t)'a')<<24 | ((uint32_t)'b')<<16 | 'c'<<8 | 
'd')
>> ...
>> case MK32(A,B,C,D):
>
>
> Das ist mal schnell schön hingeschrieben, geht aber nicht !!!
>
>
>
>     Beitrag melden | Bearbeiten | Löschen |

geht bei gcc, OK, aber nicht allen Compilern

von Falk B. (falk)


Lesenswert?

@  Rolle (Gast)

>Das ist mal schnell schön hingeschrieben, geht aber nicht !!!

Weil der Peter ein paar Striche vergessen hat. So sollte es laufen.
1
#define MK32(a,b,c,d) (((uint32_t)'a')<<24 || ((uint32_t)'b')<<16 || 'c'<<8 || 'd')
2
...
3
case MK32(A,B,C,D):

MFG
Falk

von (prx) A. K. (prx)


Lesenswert?

Falk Brunner schrieb:

> Weil der Peter ein paar Striche vergessen hat. So sollte es laufen.

= "case 1:"

von Michael R. (dj_motionx)


Lesenswert?

1
#define MAKEK32(a,b,c,d) (((unsigned int)'a')<<24 || ((unsigned int)'b')<<16 || 'c'<<8 || 'd')
2
3
int main(void) {
4
5
  unsigned int  x;
6
  x = (((unsigned int)'X')<<24 || ((unsigned int)'Y')<<16 || 'Z'<<8 || 'A'); //Test
7
  switch (x) {
8
9
    case MAKE32(A,B,C,D):  printf("Falscher case");
10
                break;
11
    case MAKE32(X,Y,Z,A):  printf("Funktioniert");
12
                break;
13
    default:        break;
14
  }
15
16
  return EXIT_SUCCESS;
17
}

Funktioniert bei mir auch mit GCC nicht. Also ich glaube auch dass die 
Textersetzung mittels Makro nicht erlaubt ist und bei der case anweisung 
nur ganzzahlige Konstanten stehen dürfen.

-> Was spricht gegen einen if-Rechen


Mfg Michi

von Weissefaust (Gast)


Lesenswert?

1
char string[5] = "abcd\0";
2
3
if(!strcmp(string, "ABCD")) {
4
5
} else if(!strcmp(string, "BCDA")) {
6
7
} else if(!strcmp(string, "DBCA")) {
8
9
} 
10
...
11
...

von Peter II (Gast)


Lesenswert?

Michael Rathmair schrieb:
> Funktioniert bei mir auch mit GCC nicht.
geht nocht oder compiliert nicht? Wenn ja welche Fehlermeldung

> Also ich glaube auch dass die
> Textersetzung mittels Makro nicht erlaubt ist und bei der case anweisung
> nur ganzzahlige Konstanten stehen dürfen.
die Preprozessor ist es egal wo er etwas ersetzt, er kennt kein switch.
nach dem Preprozessor steht ja dort eine Konstante.

von (prx) A. K. (prx)


Lesenswert?

Peter II schrieb:

> die Preprozessor ist es egal wo er etwas ersetzt,

Fast. Die Frage ist, ob er auch innerhalb von Strings ersetzt:
1
#define f(a) a 'a' "a"
2
f(x)
ergibt beim GNU CPP
1
x 'a' "a"

von Michael R. (dj_motionx)


Lesenswert?

Oh sorry, sorry sorry für die Übereile.
Man sollte mal richtig tippen können.

Michael Rathmair schrieb:
> #define MAKEK32(a,b,c,d) (((unsigned int)'a')<<24 || ((unsigned int)'b')<<16 || 
'c'<<8 || 'd')

muss natürlich
#define MAKE32(a,b,c,d) (((unsigned int)'a')<<24 || ((unsigned 
int)'b')<<16 || 'c'<<8 || 'd')

sein.
1
#define MK32(a,b,c,d) (((unsigned int)'a')<<24 || ((unsigned int)'b')<<16 || 'c'<<8 || 'd')
2
...
3
unsigned int  x;
4
  x = (((unsigned int)'X')<<24 || ((unsigned int)'Y')<<16 || 'Z'<<8 || 'A'); //Test
5
6
  switch (x) {
7
8
    case MK32(A,B,C,D):  printf("Falscher case");
9
                break;
10
    case MK32(X,Y,Z,A):  printf("Funftioniert");
11
              break;
12
    default:        break;
13
  }
bringt trotzdem duplicate case value

Mfg

von (prx) A. K. (prx)


Lesenswert?

Michael Rathmair schrieb:

> bringt trotzdem duplicate case value

Klar doch. (x || 'A') ist immer 1.

von Michael R. (dj_motionx)


Lesenswert?

A. K. schrieb:
> Klar doch. (x || 'A') ist immer 1.

So gesehen schon dann funktioniert die Lösung aber doch nicht richtig 
?????

von (prx) A. K. (prx)


Lesenswert?

Michael Rathmair schrieb:

> So gesehen schon dann funktioniert die Lösung aber doch nicht richtig

"||" ist ja auch Blödsinn.

Was geht:
1
define(`MK32',('$1'<<24|'$2'<<16|'$3'<<8|'$4'))
2
case MK32(a,b,c,d):
und das durch den M4 Präprozessor jagen. Gibt:
1
case ('a'<<24|'b'<<16|'c'<<8|'d'):

von Peter D. (peda)


Lesenswert?

Rolle schrieb:
> Das ist mal schnell schön hingeschrieben, geht aber nicht !!!

Stimmt, die ' sind falsch, es geht nur so:
1
#define MK32(a,b,c,d) (((uint32_t)a)<<24 | ((uint32_t)b)<<16 | c<<8 | d)
2
...
3
case MK32('A','B','C','D'):


Peter

von ole (Gast)


Lesenswert?

Michael Rathmair schrieb:
> -> Was spricht gegen einen if-Rechen
Beim einen werden Zahlen verarbeitet, beim anderen knechtet er mit 
Strings. Das ist beim PC relativ egal, ein posierlicher µC hat daran 
verdammt viel zu tun.
Egal ob PC oder nicht, solange es ohne String-geklöppel geht, würde ich 
es ohne machen.

Im übrigen sollte die Textersetzung der GCC gar nicht mitbekommen? Der 
Präprozessor kommt doch vor dem Compiler.

Michael Rathmair schrieb:
> A. K. schrieb:
>> Klar doch. (x || 'A') ist immer 1.
>
> So gesehen schon dann funktioniert die Lösung aber doch nicht richtig
> ?????
Wo ist denn der Unterschied zwischen '|' und '||'?
oder
Wo ist denn der Unterschied zwischen '&' und '&&'?

von U.R. Schmitt (Gast)


Lesenswert?

Lest mal ein C Buch und findet heraus was der Unterschied zwischen einem 
'|' und einem '||' ist.
Ich würde im Zweifel aber auch die if then else if ... Kette bevorzugen.

von U.R. Schmitt (Gast)


Lesenswert?

Uups A.K., Peter und ole waren schneller :-)

von Michael R. (dj_motionx)


Lesenswert?

Peter Dannegger schrieb:
> Stimmt, die ' sind falsch, es geht nur so:#define MK32(a,b,c,d) 
(((uint32_t)a)<<24 | ((uint32_t)b)<<16 | c<<8 | d)
> ...
> case MK32('A','B','C','D'):

Das sieht gut aus !

ole schrieb:
> Wo ist denn der Unterschied zwischen '|' und '||'?
> oder
> Wo ist denn der Unterschied zwischen '&' und '&&'?

Oh je nochmal sorry. Klar weiß ich dass das '||' eine ein logische und 
das '|' eine bitweise Operation ist und in diesem Fall eine bitweise 
Operation richtig ist.

von U.R. Schmitt (Gast)


Lesenswert?

Michael Rathmair schrieb:
> Oh je nochmal sorry. Klar weiß ich dass das '||' eine ein logische und
> das '|' eine bitweise Operation ist
Wobei du nicht der erste warst dem der Fehler passiert ist, das war wohl 
Falk, aber dem müssen wir nicht den Unterschied erklären :-)
Macht halt jeder mal einen Fehler, vor allem wenn man wie bei so Fragen 
mal schnell 'trocken' codiert, also ohne das dann zu compilieren 
und/oder auszuprobieren.

von Falk B. (falk)


Lesenswert?

@  U.R. Schmitt (Gast)

>> Oh je nochmal sorry. Klar weiß ich dass das '||' eine ein logische und
>> das '|' eine bitweise Operation ist
>Wobei du nicht der erste warst dem der Fehler passiert ist, das war wohl
>Falk, aber dem müssen wir nicht den Unterschied erklären :-)

Ohhhh vor den kopfklatsch.

Da hatte ich wohl einen Dreher im Hirn. Und ich hab mich gestern ne 
halbe Stunde gefragt, warum bei meiner Simulation Unsinn rauskommt. 
Naja.

MFG
Falk

von Johannes K. (joee)


Lesenswert?

Peter Dannegger schrieb:
1
 #define MK32(a,b,c,d) (((uint32_t)'a')<<24 | ((uint32_t)'b')<<16 |
2
 'c'<<8 | 'd')
3
 ...
4
 case MK32(A,B,C,D):

Das gefällt mir so ganz gut (natürlich inkl. Anführungszeichen an der 
richtigen Stelle ;-), aber mir ist noch etwas eingefallen:

Das ganze soll im Sinne von Modem-AT-Befehlen arbeiten. Wenn ich nun 
aber ca. 100 Befehle bearbeite, wird immer die komplette 4-Byte Variable 
verglichen. Ich weiß zwar nicht, was ein "if-Rechen" ist, aber ich 
glaube, dass damit ein Zweig im Sinne eines Stammbaumes gemeint ist. 
Jedenfalls führt letzteres zu einer enorm schnelleren Verarbeitung, da 
zB. viele Befehle mit 's' bzw. 'g' (set/get) beginnen:

1
switch (cmd>>24) {
2
3
    case 'A':  
4
5
        switch (cmd>>16) {
6
7
            case 'C':  printf("AC");
8
                        break;
9
            case 'D':  printf("AD");
10
                        break;
11
            default:   printf("Ax");
12
                        break;
13
          }
14
15
    case 'B':  
16
17
        switch (cmd>>16) {
18
19
            case 'C':  printf("BC");
20
                        break;
21
            case 'D':  printf("BD");
22
                        break;
23
            default:   printf("Bx");
24
                        break;
25
          }
26
    default:   printf("xx");
27
                break;
28
  }

Ich hatte gehofft, dass der super gcc auch dieses herausfindet und somit 
den ersten Buchstaben nur einmal vergleicht usw., tut er aber nicht. Es 
bleibt mir also wohl doch nur die Möglichkeit, die Befehle aufzudröseln.

Allen Beteiligten vielen Dank für die Anregungen.

von (prx) A. K. (prx)


Lesenswert?

Johannes K. schrieb:

> Ich hatte gehofft, dass der super gcc auch dieses herausfindet und somit
> den ersten Buchstaben nur einmal vergleicht usw., tut er aber nicht. Es
> bleibt mir also wohl doch nur die Möglichkeit, die Befehle aufzudröseln.

Du kannst dir auch mal flex anschauen:
de.wikipedia.org/wiki/Lex_(Informatik)
en.wikipedia.org/wiki/Flex_lexical_analyser

von Stefan E. (sternst)


Lesenswert?

Johannes K. schrieb:
> den ersten Buchstaben nur einmal vergleicht usw., tut er aber nicht.

Mir ist zwar nicht klar, was genau du damit meinst, aber bei deinem 
Beispiel fehlen
1) die breaks hinter den "Sub-Switches"
2) das Ausmaskieren eines Zeichens im switch ((cmd>>16) ist nicht das 
zweite Zeichen, sondern die ersten beiden)

Zusatz zu 2): wenn du jeweils nach einzelnen Buchstaben vorgehst, wozu 
dann diese vorher überhaupt zu einem uin32_t kombinieren?

von Johannes K. (joee)


Lesenswert?

Stefan Ernst schrieb:

> Mir ist zwar nicht klar, was genau du damit meinst, aber bei deinem
> Beispiel fehlen
> 1) die breaks hinter den "Sub-Switches"

Da hast du natürlich Recht.

> 2) das Ausmaskieren eines Zeichens im switch ((cmd>>16) ist nicht das
> zweite Zeichen, sondern die ersten beiden)

Ich meine, dass er immer nur 1 Byte benutzt, da ich ja mit nur 1 
vergleiche. Daher verwendet der Compiler dann doch das zweite.

>
> Zusatz zu 2): wenn du jeweils nach einzelnen Buchstaben vorgehst, wozu
> dann diese vorher überhaupt zu einem uin32_t kombinieren?

Mache ich dann natürlich nicht, war jetzt im Beispiel einfacher.

von Stefan E. (sternst)


Lesenswert?

Johannes K. schrieb:
> Ich meine, dass er immer nur 1 Byte benutzt, da ich ja mit nur 1
> vergleiche. Daher verwendet der Compiler dann doch das zweite.

Da meinst du falsch. Du vergleichst keine einzelnen Bytes, sondern 
komplette Zahlen.
1
uint16_t a = 0xabcd;
2
uint8_t b = 0xcd;
3
4
if (a==b) ...
Der Vergleich ist natürlich false, und nicht etwa (wie du meinst) true, 
nur weil der untere Teil von a mit b übereinstimmt.

von Johannes K. (joee)


Lesenswert?

Stefan Ernst schrieb:
> Da meinst du falsch. Du vergleichst keine einzelnen Bytes, sondern
> komplette Zahlen.

Ok, hast natürlich wieder Recht. Ich würde also noch nach uint8_t casten 
müssen, wenn ich es so verwenden wollte. Richtig?

Meine Lösung sieht nun so aus (Auch, wenn mir Vergleiche mit "SE", "SD", 
usw. lieber gewesen wäre):

1
void ConfigCheckCommand()
2
{
3
    if (UART0_receive_line())
4
    {
5
        switch (uart0_rx[0])
6
        {
7
        case 'S':
8
9
            switch (uart0_rx[1])
10
            {
11
            case 'E':
12
                HMPowerOn();
13
                break;
14
            case 'D':
15
                HMPowerOff();
16
                break;
17
            case 'R':
18
                LampenEnable(&raumlicht, 1);
19
                break;
20
            case 'L':
21
                LampenEnable(&leselampe, 1);
22
                break;
23
            }
24
25
            break;
26
27
        case 'G':
28
29
            switch (uart0_rx[1])
30
            {
31
            case 'P':
32
                UART0TransmitByte(power_on);
33
                break;
34
            case 'R':
35
                UART0TransmitByte(LampenGet(&raumlicht));
36
                break;
37
            case 'L':
38
                UART0TransmitByte(LampenGet(&leselampe));
39
                break;
40
            }
41
42
            break;
43
        }
44
    }
45
}

von Johannes K. (joee)


Lesenswert?

A. K. schrieb:
> Du kannst dir auch mal flex anschauen:
> de.wikipedia.org/wiki/Lex_(Informatik)
> en.wikipedia.org/wiki/Flex_lexical_analyser

Was willst du mir damit sagen?
Dass der GCC es doch erkennt und alle 'Axxx' zusammenfast und alle 
'Bxxx'?

von (prx) A. K. (prx)


Lesenswert?

Johannes K. schrieb:

> Was willst du mir damit sagen?
> Dass der GCC es doch erkennt und alle 'Axxx' zusammenfast und alle
> 'Bxxx'?

Lex/Flex sind Codegeneratoren, denen man ein spezielle Quellfile 
vorsetzt und die daraus C Code produzieren. Das Ergebnis ist eine 
C-Funktion, die nicht mehr einzelne Zeichen zurück gibt, sondern fertige 
Tokens die für eine beliebige Anzahl Zeichen eines bestimmten Musters 
stehen. Und das recht zeiteffizient.

Aus der Lex-Beschreibung
1
"ABCD"  { return 1; }
2
"xyz"   { return 2; }
3
[0-9]+  { yylval = atoi(yytext); return 3; }
4
.       { crash("Trottel!"); }
wird Code erzeugt, der aus einem irgendwoher sequentiell gelesenen 
Zeichenstrom beim String "ABCD" den Wert 1 zurück gibt, bei "xyz" 2, bei 
jeder nicht negativen dezimalen Ganzzahl 3 (und deren Wert sich in der 
Variable yylval befindet). Die letzte Zeile ist sowas wie das default: 
vom switch.

Das sind gern benutze Tools, um Programmier- oder Kommandosprachen in 
ihre lexikalischen Einzelteile wie Schlüsselworte, Zahlen, Strings und 
Namen zu zerlegen.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.