Hallo,
ich habe ein rel. grosses Programm (Für Hobby-Verhältnisse ;-) ) in
welchem ein int auf (momentan noch) 53 Zustände geprüft werden soll.
Das int ist dabei ein Indexbyte (einfach nur das erste Byte der
Nachricht) einer 8-Byte UART Nachricht welche ich an meinem µC empfange.
Momentan habe ich die Zuständen (0x00 bis 0x34) als define deklariert
und frage das int dann im Programm ab:
1
if(empfangenes_Index_Byte==PARAMETER001)
2
{
3
....
4
}
5
elseif(empfangenes_Index_Byte==PARAMETER002)
6
{
7
....
8
}
9
elseif(empfangenes_Index_Byte==PARAMETER003)
10
{
11
....
12
}
usw. usf.
Nun hab ich mir überlegt ob ich das ganze nicht als switch-case umbaue,
ich kenne aber nicht den exakten Unterschied zwischen den beiden
Abfragen, was wäre für mich besser?
Anderseits, und hier beginnt mein eigentliches Problem, habe ich mir
auch schon überlegt das ganze komplett anderst zu machen, die 53
Zuständen werden später vlt. noch erweitert und bereits jetzt ist das
ganze ein ziemlicher Spaghetti-code.
In den jeweiligen If-Bedingungen werden zum einen Funktionen aufgerufen
die in allen Bedingungen vorkommen und z.T. wird aber auch eine
individuelle Bitmanipulation vorgenommen.
Hier mal ein Beispiel:
1
elseif(empfangenes_Byte==PARAMETER021)
2
{
3
n=Ueberpruefe_Laenge(n);
4
i=Ueberpruefe_CRC(i);
5
if(n==OK)
6
{
7
if(i==OK)
8
{
9
sende(Stellung_Weiche_3);
10
}
11
}
12
}
Parameter 21 hier z.B. soll die Stellung einer Eisenbahnweiche zurück
senden. Länge und CRC-Prüfung ist überall drin, aber eben z.B. das sende
der Weichenstellung ist individuell, in einer anderen Bedingung schalte
ich z.B. LED's ein (Hier könnte also "alles" stehen: Parameter lesen,
Zustände setzen, weitere Funktionen aufrufen...)
Gibt es hierfür nicht "irgendetwas" anderes als eben else-if oder
switch-case abfragen? Wie machen das die Profis, wenn ich keine 53
sondern 1000 mögliche Zustände habe, dann kann ich doch nicht eine
switch-case mit 1000 Abfragen ausführen?
Da leidet ja die Übersicht extrem darunter und schon jetzt bei meinen 53
Abfragen muss ich ewig hoch und runter scrollen bis ich mal an der
Stelle bin die ich gerade suche!
Mir ist das ganze eben viel zu unübersichtlich, auch auf zukünfige
Sicht, welche Möglichkeiten hätte ich ausser else-if und/oder
switch-case, bzw. wo genau liegt denn der Unterschied zwischen den
beiden Abfragen?
Hallo,
wie du oben schon selbst schreibst, sind 53 if-else Verschachtelungen
nicht gerade leserlich.
Der wesendliche Unterschied zwischen if-else und switch-case ist, daß
man bei if-else Konstrukten beliebige boolsche Ausdrücke prüfen kann,
bei switch-case wird immer nur die Variable im SWITCH mit exakt dem Wert
von CASE verglichen. Konstrukte wie z.B. if ((var > 10) && (var <20))
sind als switch-case eher umständlicher (Stichwort Fall-Through).
Der Vorteil von switch-case ist, daß der Compiler den Code viel besser
optimieren kann wie das bei einzelnen if-else Statements möglich ist.
Der Grund dafür ist, daß er ja weiß, daß immer die selbe Variable
geprüft wird, und daß der Vergeich immer mit einem eindeutigen
konstanten Wert erfoglt und nicht von anderen Bedinungen abhängt.
Der Compiler kann bei größeren switch-case Konstrukten optimieren, indem
er das ganze als indizierte Sprungtabelle implementieren kann. Das geht
bei if-else Anweisungen nicht.
Unter den genannten Gesichtspunkten (Code-Übersichtlichkeit,
Compiler-Optimierung) würde ich für deinen Fall das switch-case
Konstrukt bevorzugen.
Gruß Bernd
Wenn Dir das switch noch zu lang ist, gibt es u.U. die Möglichkeit,
eine entsprechend große Tabelle mit Zeigern auf Funktionen anzulegen,
und das empfangenes_Index_Byte als Index in diese Tabelle zu verwenden,
um die jeweils passende Funktion aufzurufen. Der Inhalt je eines
case-Zweiges geht dann in je eine Funktion.
Das geht aber nur, wenn die Funktionen auf eine einheitliche
Parameterliste zu kriegen sind (was je nach Fall mal besser oder
schlechter oder gar nicht geht).
Wenn es geht, ist es aber unschlagbar schnell und elegant.
(Nur wer keine Funktionszeiger mag, wird es hassen).
Condi schrieb:
> Switch Case ist hier das bessere. Alternativ eine Funktion schreiben und> diese in einer Lookuptabelle hinterlegen(wie bei den Interruptvektoren).
genau das meinte ich
Das switch-Case-Zeug ist aber wunderbar schnell, man wirft eine Zahl
rein und kommt direkt bei nur einer Möglichkeit raus.
Bei else-if geht er natürlich alle Abfragen durch bis er zur relevanten
ankommt, das dauert seine Weile.
else-if benutze ich nur wenn ich diverse Bedingungen miteinander
verknüpfe und mehr als nur eine Zahl habe.
Eine der beiden Möglichkeiten wirst du wohl benutzen müssen, aber man
kann es ja in eine Funktion packen und die in eine andere Datei, dann
bleibt das auch übersichtlich.
In deinem zweiten beispiel könntest du auch beide Bedingungen in einer
If-Abfrage bearbeiten:
Gut ist bei switch case die Variant mit default.
Dort sammeln sich dann die Fälle die man nicht explizit
aufgeführt hat. Gut auch um Fehler abzufangen.
avr
Wenn Du z.B. 1000 verschiedenen Zustände hast, dann wird es auch immer
auf 1000 Überprüfungen hinauslaufen... ob nun mit if-else oder
switch-case!
Um die Übersichtlichkeit zu erhöhen, könntest Du aber die verschiedenen
Funktionen, die ausgeführt werden sollen, in logische Gruppen oder
Befehlskategorien zusammenfassen.
Z.B. Aktion ausführen, Status abfragen, etc.
Innerhalb der Befehlskategorien werden dann Untergruppen aktiviert:
z.B. Aktionen ausführen -> Weiche x stellen, LED y anmachen...
Stefan schrieb:
> Wenn Du z.B. 1000 verschiedenen Zustände hast, dann wird es auch immer> auf 1000 Überprüfungen hinauslaufen... ob nun mit if-else oder> switch-case!
Falsch wie oben schon geschrieben, das glit nur beim ifelse
beim Switch wird einmal geprüft und dann passend gesprungen
>Stefan schrieb:>> Wenn Du z.B. 1000 verschiedenen Zustände hast, dann wird es auch immer>> auf 1000 Überprüfungen hinauslaufen... ob nun mit if-else oder>> switch-case!>Falsch wie oben schon geschrieben, das glit nur beim ifelse>beim Switch wird einmal geprüft und dann passend gesprungen
Im Programm-Code wirst Du aber 1000 Überprüfungen finden... da kommst
auch Du nicht drum herum! Und das ist unübersichtlich. Ich habe nur
versucht darzustellen, wie man das übersichtlicher gestalten könnte!
Dachte, bei switch wird das Sprungziel abhängig vom Wert der Variable
bestimmt? Bzw. der zu prüfende Wert wird als Offset für die Zieladresse
verwendet?
>Falsch wie oben schon geschrieben, das glit nur beim ifelse>beim Switch wird einmal geprüft und dann passend gesprungen
Wie soll das denn gehen bitteschön, die einsprungpunkte sind doch nicht
gleichmäßig verteilt.
Wenn du es nicht glaubst Dann schau dir mal den Assemblercode an.
... ... schrieb:
> Stefan schrieb:>> Wenn Du z.B. 1000 verschiedenen Zustände hast, dann wird es auch immer>> auf 1000 Überprüfungen hinauslaufen... ob nun mit if-else oder>> switch-case!>>> Falsch wie oben schon geschrieben, das glit nur beim ifelse> beim Switch wird einmal geprüft und dann passend gesprungen
Schreib bitte dem Compiler nicht vor, wie er einen switch-case zu
implementieren hat. Gefordert ist, dass bei einem switch-case die
richtige Alternative ausgeführt wird. Wie der Compiler das macht ist
seine Sache. Wenn die Werte günstig liegen, kann er dazu eine
Spruntabelle nehmen, wenn sie ungünstig liegen kann er dazu eine ganz
normale if-else Prüfkette aufbauen.
Eine klassische if-else Kette in der Implementierung von switch Befehlen
wird man allenfalls bei sehr wenig Werten finden. Wenn sich eine Tabelle
nicht lohnt, wird ein Compiler oft einen Vergleichsbaum verwenden, d.h.
die Laufzeit wächst logarithmisch statt linear.
Sowas ist manuell einigermassen schwierig.
> Wie machen das die Profis, wenn ich keine 53> sondern 1000 mögliche Zustände habe
Sie überlegen sich zum Beispiel, wie sie die Logik in eine Tabelle
pressen können.
Das kann seine eine Tabelle von Funktionspointern. Das kann aber auch
sein eine Tabelle in der zb eine bestimmte Nummer für eine bestimmte
Aktion steht und wieder eine andere Nummer für ein Argument, oder
Kombinationen aus beidem.
1
structAction
2
{
3
uint16_tCommandNr;
4
uint16_tActionNr;
5
uint16_tVariableNr;
6
};
7
8
structActionactions[]
9
{
10
....
11
{PARAMETER021,actionSend,Stellung_Weiche_3},
12
....
13
};
Wird ein Byte empfangen, geht eine Schleife die Aktionen durch (oder
benutzt den empfangenen Parameter als Index ins Array) und holt sich die
auszuführende Aktion (hier actionSend) und worauf diese Aktion
ausgeführt werden soll (hier Stellung_Weiche_3)
Eine neue Aktion einzuführen ist dann einfach nur ein neuer Eintrag in
dieser Tabelle.
Oder eine andere Möglichkeit:
Anstatt nur einem Byte übertragen sie 2 Bytes um von der Gegenstelle
etwas anzufordern. Das erste Byte ist zb ein Kommandocode (zb für
senden) und das zweite Byte ist ein Code womit diese Aktion ausgeführt
werden soll.
Deine 53 Zustände schrumpfen dann wahrscheinlich auf 3 oder 4 Aktionen
und 10 oder 12 Objektbezeichner zusammen.
Weil wir heuer 40 Jahre Mondlandung haben: So haben zb. die Astronauten
mit ihrem Bordcomputer kommuniziert: Es gab ein Verb-Noun System. Jedes
Verb war einfach nur eine Zahl und stand für eine Aktion.
zb 05 .... zeige oktal
06 .... zeige dezimal
....
Jedes Noun war die Bezeichnung für ein Objekt (diverse Anzeigeformen
wurden durch unterschiedliche Nouns repräsentiert)
09 .... Alarm Codes
36 .... current Time
.... aktuelle Position
.... Zeit bis zur nächsten Zündung
etc
Wollte ein Astronaut wissen, wie spät es ist (gemessein in Mission Time)
dann drückte er
Taste "Verb" Taste "0" Taste "6" Taste "Noun" Taste "3" Taste "6" Taste
"Enter" oder kurz V06N36E
und der Computer verstand das als "aktuelle Zeit anzeigen"
Als während der Landung am Computer das Warnlicht für "Program Alarm"
aufleuchtete, drückte Aldrin V05N09E, der Computer zeigte auf seiner
7-Seg Anzeige 1202 an. Aldrin fragte in Houston nach und von dort kam
das "GO" - die Landung fortsetzen, wir können den Fehler ignorieren.
A. K. schrieb:
> Eine klassische if-else Kette in der Implementierung von switch Befehlen> wird man allenfalls bei sehr wenig Werten finden. Wenn sich eine Tabelle> nicht lohnt, wird ein Compiler oft einen Vergleichsbaum verwenden, d.h.> die Laufzeit wächst logarithmisch statt linear.
Kann natürlich auch sein.
Auf jeden Fall ist die Aussage: "beim Switch wird einmal geprüft" so
nicht richtig.