MoinMoin,
im Rahmen meines GameOfLife Gebastels stellt sich mir eine Frage.
Ich habe eine Variable, die ich als Satz von Flags benutze. In einem
Case Switch wollte ich nun auswerten, ob ein oder mehrere Sachen
passieren sollen.
Gedacht hatte ich mir das folgendermaßen:
1
#define Blah 0
2
#define Blub 1
3
4
uint8_tFlags=0;
5
6
switch(Flags)
7
{
8
case((Flags&(1<<Blah))):dosomething();break;
9
case((Flags&(1<<Blub))):dosomethingelse();break;
10
default:break;
11
}
ich möchte also, dass jedes Bit der Flags für einen Case steht.
So geht das aber nich, meckert der Compiler. Also hab ich das erstmal so
gelöst:
1
#define Blah 1
2
#define Blub 2
3
4
uint8_tFlags=0;
5
6
switch(Flags)
7
{
8
case(Blah):dosomething();break;
9
case(Blub):dosomethingelse();break;
10
default:break;
11
}
So müsste ich dann aber auch noch einen Case für den Fall dass Blah und
Blup gesetzt sind machen. Wenn ich dann nachher 8 Sachen zu setzen habe,
müsste ich also 256 Cases schreiben. Dann kann ich ja genauso gut ein
If-Monster bauen.
Gibt es da einen effektiven Weg, sowas bitweise auszuwerten?
MfG Chaos
Du kannst für /case/-Zweige nur Konstanten verwenden. Dazu gehören auch
Ausdrücke, die zur Übersetzungszeit berechnet werden.
Dazu kommt, daß Dein erster Ansatz nicht eindeutig ist:
Wenn beide Bits gesetzt sind, müssten beide case-Zweige abgearbeitet
werden. So aber arbeitet switch/case nicht.
Du wirst um Dein if-"Getüm" nicht herumkommen.
Schade...
Danke dir trotzdem für deine, wenn auch unerfreuliche, Antwort.
Rufus Τ. F. schrieb:> Wenn beide Bits gesetzt sind, müssten beide case-Zweige abgearbeitet> werden. So aber arbeitet switch/case nicht.
und für diese Klarstellung.
Eigentlich auch logisch, des breaks; wegen...
Dann aber doch lieber 8ifs statt 256 cases :D
J. T. schrieb:> Eigentlich auch logisch, des breaks; wegen...
Nein, eigentlich ist es wegen Sprungtabelle oder Binärbaum, wenn
ich mich richtig erinnere.
Deswegen ist es auch schneller als if - alle Sprungadressen stehen
schon bei Kompilieren fest.
Marc V. schrieb:> Deswegen ist es auch schneller als if
Wie der Compiler es umsetzt, ist nicht spezifiziert. Er kann das auch
mit if/else machen. Sprungtabelle ist nur dann wahrscheinlich, wenn es
sich um einen sehr begrenzten Wertebereich handelt, den man überhaupt
mit einer Sprungtabelle sinnvoll hinkriegen kann.
Deswegen sollte man mit switch vorsichtig sein, wenn es um Performance
geht, weil eine kleine Änderung überraschend große Folgen haben kann.
Nop schrieb:> Sprungtabelle ist nur dann wahrscheinlich, wenn es> sich um einen sehr begrenzten Wertebereich handelt
Da mit der Tabelle ein gewisser Overhead verbunden ist, müssen genug
Fälle zusammen kommen, damit es sich lohnt.
Wobei switch mitunter einen Abfrage-Baum implementiert, was ein
Programmierer mit if kaum zustande bekommt.
J. T. schrieb:> So müsste ich dann aber auch noch einen Case für den Fall dass Blah und> Blup gesetzt sind machen. Wenn ich dann nachher 8 Sachen zu setzen habe,> müsste ich also 256 Cases schreiben.
Du bist da gedanklich schon auf den richtigen Weg. Es sind nur noch ein
paar Schritte zu gehen.
> Dann kann ich ja genauso gut ein If-Monster bauen.
Nur daß das - zumindest im Vergleich zu einem switch() mit 256 cases -
eben gerade kein Monster wird.
Switch|case vergleicht eine Variable mit einer Liste von Konstanten,
es kann nur maximal einer der cases genommen werden. Es kann zwar
trotzdem mehr als einer der Codeblöcke ausgeführt werden, entweder weil
man ein break vergessen hat oder weil man dieses fall through
tatsächlich wollte - aber das ist eine andere Geschichte.
Der Punkt ist: Bits in einem Byte sind unabhängig voneinander. Deswegen
ergibt es auch keinen Sinn, mit switch|case einzelne Bits testen zu
wollen. Der normale, sinnvolle Weg sind 8 unabhängige if|then|else
Konstrukte.
Inwiefern das dann zu deiner Programmlogik paßt, ist wieder eine andere
Sache. Speziell für Game Of Life brauchst du die Anzahl der gesetzten
Bits. Und die kann zwischen 0 und 8 liegen. Und da sind wirklich nur 9
einzelne Fälle zu betrachten. Es kommt nicht darauf an, auf welche
Bits jeweils gesetzt sind. Insofern ist switch|case schon das richtige
Konstrukt, nur darfst du dem nicht die Bitmaske direkt verfüttern,
sondern mußt vorher die Anzahl der gesetzten Bits zählen.
Das ist eine durchaus häufig gebrauchte Operation, aber leider hat C
keinen Operator dafür. Manche CPU haben aber Assembler-Instruktionen
dafür und dann stellt der C-Compiler meist eine "builtin" Funktione zum
Zugriff auf diese Operation bereit. Von C-Seite sieht das dann wie ein
FUnktionsaufruf aus, der Compiler macht daraus aber u.U. nur eine
einzige Instruktion.
Aber auch ohne das, haben sich seit vielen Jahren schon Programmierer
darüber Gedanken gemacht, wie man diese Operation effizient hinkriegt.
Das Internet kann helfen. Guckst du z.B. hier:
https://stackoverflow.com/questions/109023/how-to-count-the-number-of-set-bits-in-a-32-bit-integer
Achim S. schrieb:> Die 8 if's sind in deinem Fall ja noch kleiner als es 8> Case-Anweisungen.wären. das
Aber es wären doch 256 cases wenn man 8 bit auszuwerten hat? Wo nimmst
du die 8 Case-Anweisungen her?
Axel S. schrieb:> Du bist da gedanklich schon auf den richtigen Weg. Es sind nur noch ein> paar Schritte zu gehen. .....
Danke dir für die Ausführungen. Den Link muss ich mir nochmal genauer zu
Gemüte führen, beim ersten Überfliegen machte es noch nicht klick :D
J. T. schrieb:> Aber es wären doch 256 cases wenn man 8 bit auszuwerten hat? Wo nimmst> du die 8 Case-Anweisungen her?
256 Fälle sind es, wenn man die Bits zusammengesetzt betrachtet. 8, wenn
jedes Bit einzeln und unabhängig von den anderen betrachtet wird.
Je nachdem, was mit den 8 Bits codiert wird, ist die eine oder die
andere Sichtweise anzuwenden.
Ohne weitere Informationen des Threadstarters darüber, was seine Bits
(und gegebenenfalls deren Kombinationen) bedeuten, lässt sich die
Problemstellung nicht abschließend lösen.
J. T. schrieb:> Aber es wären doch 256 cases wenn man 8 bit auszuwerten hat? Wo nimmst> du die 8 Case-Anweisungen her?
Es bezog sich auf Dein
J. T. schrieb:> If-Monster
Die gibt es aber sowieso nicht. Was es gibt sind if / else if -Monster.
Die entsprechend einem Case und haben die "Switch" Anweisung in jeder
Abfrage wiederholt.
Mir ging es also darum: Wenn Du das mit if hingeschrieben hättest, wäre
der Code schöner als das, was Du Dir mit case erhofft hast.
Achim S. schrieb:> J. T. schrieb:>> Aber es wären doch 256 cases wenn man 8 bit auszuwerten hat? Wo nimmst>> du die 8 Case-Anweisungen her?>> Es bezog sich auf Dein>> J. T. schrieb:>> If-Monster>> Die gibt es aber sowieso nicht. Was es gibt sind if / else if -Monster.
Wenn man 8 unabhängige Bits testet, dann auch nicht. Es sind dann 8
unabhängige if()s. Nicht geschachtelt oder so.
Aber gut, wenn die Bits den Zustand der Nachbarn in einem Game Of Life
Gitter repräsentieren, dann will man die Bits ja gar nicht unabhängig
betrachten. Was man für die Berechnung des Folgezustands braucht, ist
die Anzahl der lebenden Nachbarn; vulgo die Anzahl der gesetzen Bits.
Wenn es nur um die Lesbarkeit geht und du gerne unbedingt Einzeiler
draus machen möchtest, könntest du auch mit Funktionspointern arbeiten.
Ich mache das nicht so oft, daher bitte Beispielcode nicht auf die
Waagschale werfen.
Hier sind die Bits 1 und 3 gesetzt in Flags, die Auswertung startet die
Funktionen print1() und print3(). Es wäre auch möglich, per returnValue
der Funktion "do_ifBitIsSet" eine Funktion auszuführen, ich habe den
eigentlichen Aufruf aber lieber in der Funktion.
Die Bits würde ich per ENUM definieren, mit Namen arbeitet es sich
einfach leichter.
Axel S. schrieb:> Switch|case vergleicht eine Variable mit einer Liste von Konstanten,> es kann nur maximal einer der cases genommen werden.
ist zwar eine doofe Idee, nie probiert, aber in C denke ich kann ich im
Case die Variable ändern dann mit goto zum switch zurückspringen.
Achim S. schrieb:> J. T. schrieb:>> Aber es wären doch 256 cases wenn man 8 bit auszuwerten hat? Wo nimmst>> du die 8 Case-Anweisungen her?>> Es bezog sich auf Dein>> J. T. schrieb:>> If-Monster>> Die gibt es aber sowieso nicht. Was es gibt sind if / else if -Monster.> Die entsprechend einem Case und haben die "Switch" Anweisung in jeder> Abfrage wiederholt.>> Mir ging es also darum: Wenn Du das mit if hingeschrieben hättest, wäre> der Code schöner als das, was Du Dir mit case erhofft hast.
Alles klar! Danke dir für die Ausführung.
Rufus Τ. F. schrieb:> 256 Fälle sind es, wenn man die Bits zusammengesetzt betrachtet. 8, wenn> jedes Bit einzeln und unabhängig von den anderen betrachtet wird.>> Je nachdem, was mit den 8 Bits codiert wird, ist die eine oder die> andere Sichtweise anzuwenden.>> Ohne weitere Informationen des Threadstarters darüber, was seine Bits> (und gegebenenfalls deren Kombinationen) bedeuten, lässt sich die> Problemstellung nicht abschließend lösen.
Das hab ich ja angedeutet mit dem "wenn dann aber Blah und Blub sind,
bräuchte ich noch nen 3ten case". Das ganze soll schon irgendwie
zusammenhängend betrachtet werden, auch wenn es nicht wirklich
zusammengehört. Ich wollte einfach die Möglichkeit haben, beim Debuggen
unterschiedliche "Figuren" einzusetzen, indem ich die entsprechende Flag
setze. Wenn ich FigurA reinhaben will, setz ich die Flag auf 0b00000001,
will ich FigurB, dann wird die Flag auf 0b00000010 gesetzt usw. Will ich
nun mehrere gleichzeitig reinschmeißen, also Flag = 0b00000011.
Meine Hoffnung war, es gäbe etwas dass wie ein Switch funktioniert, aber
mit Variablen umkann. Gibt es aber nicht, wie ja nun geklärt wurde. Was
mir nun in Zusammenhang mit der Funktion des Case Switch auch irgendwie
einleuchtet. Bei Variablen könnten ja "nicht-vordefinierte-"Cases""
rauskommen.
Axel S. schrieb:> Aber gut, wenn die Bits den Zustand der Nachbarn in einem Game Of Life> Gitter repräsentieren, dann will man die Bits ja gar nicht unabhängig> betrachten. Was man für die Berechnung des Folgezustands braucht, ist> die Anzahl der lebenden Nachbarn; vulgo die Anzahl der gesetzen Bits.
Hat in diesem Fall aber, wie oben angemerkt, nichts mit den Nachbarn zu
tun. Auch wenn es in einem GoL ein naheliegender Gedanke ist.
Stromverdichter schrieb:> #include <stdio.h>> #include <stdint.h>>>> typedef void (*functionToPointAt)(void);>> void do_ifBitIsSet(uint16_t flags, uint16_t bitToTest, functionToPointAt> function)> {> if (flags&(bitToTest<<1))> function();> }>> void print1(void){> printf("Funktion 1 wurde aufgerufen\n");> }>> void print2(void){> printf("Funktion 2 wurde aufgerufen\n");> }>> void print3(void){> printf("Funktion 3 wurde aufgerufen\n");> }>> int main()> {> uint16_t flags = 0x3;> do_ifBitIsSet(flags, 1, print1);> do_ifBitIsSet(flags, 2, print2);> do_ifBitIsSet(flags, 3, print3);> }
Vom sich lesen lassen ist das genau das, was mir vorschwebte. Aber nach
ein wenig überlegen prüft dass ja auch alles einzeln durch. Aber daran
führ wohl kein Weg vorbei. Dennoch gefällt mir deine Formulierung
ziemlich gut und ich werd versuchen, sie mir zu merken und zu benutzen!
Danke dir dafür.
Stromverdichter schrieb:> Wenn es nur um die Lesbarkeit geht und du gerne unbedingt Einzeiler> draus machen möchtest, könntest du auch mit Funktionspointern arbeiten.
Eigentlich ging es mir darum, um die If-Abfragen drumherumzukommen. Aber
umso mehr ich überlege, ist das rein logisch gar nicht möglich.
Nebenbei, wie bekomme ich denn einen Pointer auf eine Funktion? Ich werd
nebenbei auch mal Google fragen, aber falls du Lust und eine gute
Erklärung hast, nur her damit. Persönlich formuliertes find ich meist
angenehmer als den Krams aus irgendwelchen Tutorials ;-)
Joachim B. schrieb:> ist zwar eine doofe Idee, nie probiert, aber in C denke ich kann ich im> Case die Variable ändern dann mit goto zum switch zurückspringen.
Klingt eigentlich erstmal ganz plausibel. Wobei ich noch nie mit goto
gearbeitet habe, weil eigentlich überall wo man hier mal ein "goto"
gelesen hat, gleich das panische Gekreische losging "goto ist BÖÖÖSE"
"lass das mit dem goto" usw :D
STK500-Besitzer schrieb:> Bei eine uint8_t Variable braucht man 8 Abfragen und eine> Schleife:for(uint8_t i = 1; i !=0; i<<1){> switch(Flags & i){
Ist das nicht dass was ich im Eröffnungspost hatte? Da steht doch die
Switchvariable auch nicht fest zur Laufzeit? Bzw damit machst du doch
Unterschiedliche Switches draus?
nehmen wir an:
Flags = 0b00000001 d.h im ersten Durchlauf steht da
switch( 0b00000001 & 0b00000001)... was steht da überhaupt???
steht da switch(die adresse von der variablen)?
oder steht da switch(der wert der variablen)?
Irgendwie verwirre ich mich grad selbst immer mehr...
boah ... schrieb:>> flags&(bitToTest<<1)
Der Teil ist soweit klar, wobei ich flags&(1<<bitToTest) schreiben würd
oder nicht? Damit maskiere ich das gewünschte Bit aus der Flag aus. Aber
das steht zur Laufzeit ja nicht fest. Je nachdem ob das Bit gesetzt ist
oder nicht, ist es gesetzt oder nicht ;-).
Ich versuche nochmal aufzudröseln was mich verwirrt.
Also die einzelnen Cases müssen konstant sein. Wenn ich nun switch(blah)
schreibe, wird nachgeschaut welchen Wert die Variable blah zur Zeit hat.
Auf die Casenummer dieses Wertes wird dann gesprungen. Wobei ein
Casenummer wie gesagt zur Laufzeit feststehen muss.
Im Switch selbst darf/muss aber eine Variable stehen. Dass heißt doch
aber dass switch(blah) und switch(blah & i) überhaupt nicht die selben
Switches sind?
Ich hoffe irgendwer versteht, was ich nicht verstehe :D
J. T. schrieb:> Also die einzelnen Cases müssen konstant sein. Wenn> ich nun switch(blah) schreibe, wird nachgeschaut> welchen Wert die Variable blah zur Zeit hat.> Auf die Casenummer dieses Wertes wird dann gesprungen.> Wobei ein Casenummer wie gesagt zur Laufzeit feststehen> muss.>> Im Switch selbst darf/muss aber eine Variable stehen.> Dass heißt doch aber dass switch(blah) und> switch(blah & i) überhaupt nicht die selben Switches> sind?
Auffassungssache.
Bei "switch(blah)" repräsentiert "blah" direkt die
Fallnummer.
Bei "switch(blah & i)" wird die Fallnummer erstmal aus
einer vorgeschalteten Abbildung bestimmt, nämlich aus
blah & i. Es gibt keine Variable, die direkt die Fallnummer
repräsentiert; daher finde ich diese Schreibweise auch
unschön. Es passiert zuviel auf einmal.
Ich würde also eher sagen: Der Switch ist derselbe, nur
sind die Fallnummern anders definiert.
boah ... schrieb:> J. T. schrieb:>> Ich versuche nochmal aufzudröseln was mich verwirrt.>> es steht doch hier schon:> Beitrag "Re: Frage zum Case Switch, bzw suche Konstrukt das nach bits schaltet">> und ob> for(uint8_t i = 1; i !=0; i<<1){> switch(Flags & i){>> oder> for(uint8_t i = 0; i < 8; i++{> switch(Flags & (1 << i)){>> ist doch egal.
Nein, ist es nicht.
Die 2. Variante funktioniert (bis auf die vergessene Klammer),
die 1. nicht, das ist eine Endlosschleife.
Die Compiler-Warnung "statement with no effect [-Wunused-value]" hat
eine Bedeutung. Oder gibt es hier immer noch Superhelden, die ohne
eingeschaltete Warnungen compilieren?
Georg
Auflösung: es muss schon "i <<= 1" heißen.
Ich glaub es hat grad klick gemacht. Mein Fehler war ja das case(blah&i)
versuchen wollte. Quasi ein Variabler case. Und das geht nicht. Ok der
Knoten hat sich aufgelöst.
Ich hatte gedanklich irgendwie die Variable direkt dem Switch
zugeordnet. Quasi "das ist DER blah-Switch".
Aber es gibt wohl einfach nur das Konstrukt switch. Dem übergibt man
dann einen Wert per Variablen. Und dann wird einfach zu dem
Wert/Fallnummer gesprungen.
Wäre aus irgendeinem blöden Grund blah immer auf dem selben Wert, sagen
wir mal 42, könnte man dann auch einfach
1
switch(42)
2
case1:break;
3
.
4
.
5
case42:blah();break;
6
default:break;
schreiben? Klar macht das aus des Programmierers Sicht keinen Sinn, aber
wäre das in diesem Fall das gleiche wie switch(blah)?
J. T. schrieb:> Das hab ich ja angedeutet mit dem "wenn dann> aber Blah und Blub sind, bräuchte ich noch> nen 3ten case". Das ganze soll schon irgendwie> zusammenhängend betrachtet werden, auch wenn> es nicht wirklich zusammengehört.> [...]> Eigentlich ging es mir darum, um die If-Abfragen> drumherumzukommen. Aber umso mehr ich überlege,> ist das rein logisch gar nicht möglich.
Ich habe Dein Problem nicht genau verstanden.
Suchst Du so eine Art Entscheidungstabellentechnik?
Also:
- mehrere zu prüfende Bedingungen,
- je Bedingung i.d.R. mehrere mögliche Belegungen,
- "schwache Besetzung" der Tabelle, d.h. es ergeben
sich rechnerisch sehr viel mehr Kombinationen, als
tatsächlich real zu behandeln sind.
Wenn ja: Das kann man behandeln, indem man in der ersten
Verfahrensstufe die Bedingungen auf eine Fallnummer
abbildet (einfach mit if/then/else) und in der zweiten
Verfahrensstufe die Fallnummer in ein switch/case
hineinsteckt, das dann zur richtigen Behandlungsroutine
verzweigt.
J. T. schrieb:> Ich glaub es hat grad klick gemacht. Mein Fehler war> ja das case(blah&i) versuchen wollte. Quasi ein> Variabler case. Und das geht nicht.
Warum soll das nicht gehen?
Der Compiler sollte damit kein Problem haben -- aber
für normal talentierte Programmierer ist das nicht
lesbar.
Welcher Fall wird angesprungen, wenn
blah = 0x55 und
i = 0xAA gilt?
Völlig offensichtlich: Fall 0 :)
> Ich hatte gedanklich irgendwie die Variable direkt> dem Switch zugeordnet. Quasi "das ist DER blah-Switch".
Naja, in der Regel wird man das genau so machen: Es gibt
eine Variable, deren Belegungen den einzelnen Fällen
zugeordnet werden sollen.
Das ist aber eine Beschränkung, die durch den Menschen
diktiert wird -- ich sehe keinen technischen Grund, warum
der Computer nicht statt der Variablen einen Ausdruck
akzeptieren sollte. Nur ist halt switch (a ^ b) extrem
beschissen zu debuggen.
> Aber es gibt wohl einfach nur das Konstrukt switch. Dem> übergibt man dann einen Wert per Variablen. Und dann wird> einfach zu dem Wert/Fallnummer gesprungen.
Richtig.
Jede mögliche Belegung der Variablen (jedes "Bitmuster")
repräsentiert einen möglichen Zweig.
Moin,
Um die Konfusion auf ein neues Level zu bringen - wie waers mit einem
Array aus Funktionspointern, um die switch-case anweisung zu "umgehen"?
1
#include <stdio.h>
2
3
static int bla(int nei) {
4
int naus=1+nei;
5
return naus;
6
}
7
8
static int blupp(int nei) {
9
int naus=2+5*nei;
10
return naus;
11
}
12
13
static int schnodder(int nei) {
14
int naus=3-2*nei;
15
return naus;
16
}
17
18
int main() {
19
static int (*functable[])(int)={schnodder,bla,blupp,schnodder,schnodder};
20
int naus,nei,whichfunction;
21
22
nei=11;
23
whichfunction=2;
24
25
naus=functable[whichfunction](nei);
26
27
printf("naus=%d\n",naus);
28
29
return 0;
30
}
da muss dann halt das Funktionspointerarray so initialisiert werden,
dass abhaengig vom Wert von whichfunction eben die "richtige" Funktion
aufgerufen wird.
Gruss
WK
Ratgeber schrieb:> Bla, Blupp, Schnodder...>> :))>> Du hast schöne Variablennamen! Da macht das Lesen von Quelltexten gleich> viel mehr Freude.
Gute Kinderstube; muß man pflegen.