Hallo zusammen
Ich habe eine funktionierende for-Schleife die den richtigen Case aus 18
Cases sucht. Wird keiner gefunde, dann ist der letzte Case der default.
1
for(i=0;i<NR_CASES-1;++i)
2
{
3
if(Cases[i].InputValue==data)
4
{
5
caseNr=i;
6
break;
7
}
8
}
Jetzt möchte ich den zuletzt gültigen Case verwenden Falls keiner
gefunden wird.
Das möchte ich so realisieren indem ich die gültige Case Nummer in
'LastCase' sichere und in 'caseNr' zurückspeichere Falls kein gültiger
Case gefunden wird.
Das habe ich mit einer else Anweisung versucht.
1
for(i=0;i<NR_CASES-1;++i)
2
{
3
if(Cases[i].InputValue==data)
4
{
5
caseNr=i;
6
LastCase=i;// gültiger Case sichern
7
break;
8
}
9
else
10
{
11
CaseNr=LastCase;// zuletzt gültiger Case zurückspeichern
12
}
13
}
Das Problem:
Bis und mit Case 17 funktionert es einwandfrei, Case 18 wird aber nicht
mehr ausgeführt (als ob es nicht vorhanden wäre).
Wenn ich jetzt aber die Anzahl Cases um eins erhöhe, d.h.
1
#define NR_CASES 18
auf
1
#define NR_CASES 19
dann funktionierts auch mit dem letzten Case 18.
Warum ist das so? Es gibt nur 18 Cases.
Danke und Gruss
Firebird
Joachim B. schrieb:> case 0 ist aber vermutlich ungültig,
Da in C/C++ Arrays nun einmal mit 0 beginnend indiziert werden, ist das
möglich aber sinnlos (frei nach Loriot).
Solange der OP nicht die Definitionen der Variablen preisgibt, bleibt
alles Kaffeesatz-Leserei.
Joachim B. schrieb:> deswegen inc't er ja vorher mit ++i nicht i++
Das ist Unsinn. An des Stelle macht das keinen Unterschied. In einer
C/C++ for-Schleife wird der Rückgabewert des Zählausdrucks ignoriert.
Nur die "Nebenwirkung" (hier: inkrementieren) wirkt. Deshalb könnte man
da auch i=i+1, nicht aber nur i+1 hinschreiben.
Georg.
Georg Gast 1 schrieb:> Solange der OP nicht die Definitionen der Variablen preisgibt, bleibt> alles Kaffeesatz-Leserei.
da stimme ich zu
> Das ist Unsinn. An des Stelle macht das keinen Unterschied. In einer> Georg.
da widerspreche ich, er incrementiert vor dem ersten Schleifendurchlauf,
das macht sehr wohl einen Unterschied ob vor (++i) oder nach (i++)
element[0] sollte nie angesprochen werden.
Joachim B. schrieb:>> Das ist Unsinn. An des Stelle macht das keinen Unterschied. In einer>> Georg.>> da widerspreche ich, er incrementiert vor dem ersten Schleifendurchlauf,> das macht sehr wohl einen Unterschied ob vor (++i) oder nach (i++)>> element[0] sollte nie angesprochen werden.
Bitte lies die Dokumentation zur for-Schleife.
Der 3. Ausdruck wird immer nach dem Schleifendurchlauf ausgeführt.
Der Wert des 3. Ausdrucks wird ignoriert.
Nur die Nebenwirkung (hier: inkrement) ist relevant.
Georg Gast 1 schrieb:> Bitte lies die Dokumentation zur for-Schleife.
danke, habe ich gemacht und bin erstaunt.
Bis jetzt dachte ich tatsächlich es wird vor Start incrementiert sonst
würde es ja nicht post/pre increment heissen.
aber natürlich, auch das ist bekannt es wird von links nach rechts
gearbeitet, also kommt das ++i erst wenn alles zu spät ist. (wenn der
preprozessor/Compiler nicht vorher optimierend eingreift)
ist mir irgendwie entfallen zumal es auch recht ungewöhnlich für eine
for Schleife ist vorher zu incrementieren.
Firebird schrieb:> Vielen Dank für eure Vorschläge. Ich habe gleich programmiert und> getestet.
warum schmeisst du nicht das elendige ++i raus, das verwirrt nur und hat
keinen Effekt wie wir sehen.
Es wird am Schleifenbeginn nicht vorher incrementiert!
Auch wenn es funktioniert, es bleibt doof lesbar.
Joachim B. schrieb:> Georg Gast 1 schrieb:>> Bitte lies die Dokumentation zur for-Schleife.>> danke, habe ich gemacht und bin erstaunt.
Sehr gut (ernsthaft).
> Bis jetzt dachte ich tatsächlich es wird vor Start incrementiert sonst> würde es ja nicht post/pre increment heissen.
Du vermischst hier 2 Sachen:
A: pre- / post-increment Operatoren
Natürlich sind die Ausdrücke j = i++; und j = ++i; verschieden was den
Inhalt von j danach betrifft. Die Wirkung auf i, nachdem der Ausdruck
abgearbeitet wurde, ist aber die gleiche (eins mehr als vorher).
B: for-Schleife
for(INITIALISIERUNG; ABBRUCHPRÜFUNG; ZÄHLAUSDRUCK) ANWEISUNG;
das bedeutet ausgeschrieben:
1. INITIALISIERUNG
2. if not ABBRUCHPRÜFUNG goto 6
3. ANWEISUNG
4. ZÄHLAUSDRUCK
5. goto 2
6. ende
Der ZÄHLAUSDRUCK wird also als eigenständige Anweisung ausgeführt, der
Wert davon wird ignoriert/weggeworfen, und da ist es egal ob der
ursprüngliche oder der inkrementierte Wert von i weggeworfen wird.
Nur die "Nebenwirkungen" sind wichtig.
Ein ZÄHLAUSDRUCK ohne Nebenwirkungen ist nutzlos.
>> aber natürlich, auch das ist bekannt es wird von links nach rechts> gearbeitet, also kommt das ++i erst wenn alles zu spät ist. (wenn der> preprozessor/Compiler nicht vorher optimierend eingreift)
Das ist wieder eine andere Baustelle. Mit dem obigen haben weder die
Ausführung von links nach rechts (die innerhalb eines Ausdrucks für
gleichrangige Operatoren und für die Anweisungen in einem Block/Verbund
gilt) noch der Präprozessor etwas zu tun.
Bitte lies ein gutes Buch über C.
Georg.
Georg Gast 1 schrieb:> Bitte lies ein gutes Buch über C.>> Georg.
hab ich doch schon öfter und das Meiste wieder vergessen
@TO lese was über abweisende und nicht abweisende Schleifen!
wenn du unbedingt willst mache
while(++i <= NR_CASES-1)
{
}
Joachim B. schrieb:> warum schmeisst du nicht das elendige ++i raus, das verwirrt nur und hat> keinen Effekt wie wir sehen.
Warum sollte er das? Es ist schlicht egal ob an dieser Stelle ++i oder
i++ steht. Es tut das gleiche. Wer C kennt weiß das.
Außerdem hat es historische Gründe, eher ++i zu schreiben. Vor ~30
Jahren, als die Compiler noch nicht so gut optimieren konnten, hat ++i
zu effizienteren Code geführt (der Compiter hat eine Hilfsvariable
angelegt, im den Ausgangswert von i zur weiteren Verwendung zu
behalten). Heute ist es auch dahingehend egal.
Georg.
Joachim B. schrieb:> Georg Gast 1 schrieb:>> Warum sollte er das? Es ist schlicht egal ob an dieser Stelle ++i oder>> i++ steht. Es tut das gleiche. Wer C kennt weiß das.>> welches C ist denn gemeint? K&R C89 C99?> https://de.wikipedia.org/wiki/Varianten_der_Programmiersprache_C
Jetzt hast Du's mir aber so richtig gezeigt. Also dass Du keine Ahnung
von C hast.
Es ist nämlich schon wieder egal. Das (und vieles andere auch) ist in
allen C Varianten gleich.
Ich bin dann mal raus.
Georg.
Georg Gast 1 schrieb:> Warum schreibst Du nicht i < NR_CASES wie alle anderen?
Wenn ich das so schreibe gibt's ein Durcheinander, d.h. 'data' Werte
werden verarbeitet obwohl sie nicht in den 18 Cases vorhanden sind. Das
Resultat sind willkürlich leuchtende LEDs.
Die Erklärung warum es
1
i<=NR_CASES-1
lauten muss weiss ich noch nicht. Die Struct sieht wie folgt aus:
1
structLedCases[NR_CASES]=
2
{
3
{0x03,{0,5,5,0,5,0,0,5,0,0,0,0}},// case 1
4
{0x83,{0,PWM_MAX,5,0,PWM_MAX,0,0,5,0,0,0,0}},// case 2
5
{0x07,{0,5,5,0,5,0,5,0,0,0,0,5}},// case 3
6
{0x87,{0,PWM_MAX,5,0,PWM_MAX,0,5,0,0,0,0,5}},// case 4
7
{0x0b,{0,0,0,0,0,0,5,0,0,0,0,5}},// case 5
8
{0x13,{0,5,0,0,5,0,0,5,0,0,0,0}},// case 6
9
{0x23,{0,5,0,0,0,0,0,5,0,0,0,0}},// case 7
10
{0x43,{5,0,0,5,0,5,0,0,0,0,0,0}},// case 8
11
{0x01,{0,5,0,0,0,0,0,5,5,0,5,0}},// case 9
12
{0x81,{0,5,0,0,0,0,0,PWM_MAX,5,0,PWM_MAX,0}},// case 10
13
{0x05,{5,0,0,0,0,5,0,5,5,0,5,0}},// case 11
14
{0x85,{5,0,0,0,0,5,0,PWM_MAX,5,0,PWM_MAX,0}},// case 12
Firebird schrieb:> Mich irritiert die Tatsache, dass der Ursprüngliche Code mit
Weil Cases[NR_CASES] nicht existiert. Memory nicht initialisiert.
Grüsse,
René
Firebird schrieb:> Georg Gast 1 schrieb:>> Warum schreibst Du nicht i < NR_CASES wie alle anderen?>> Wenn ich das so schreibe gibt's ein Durcheinander, d.h. 'data' Werte> werden verarbeitet obwohl sie nicht in den 18 Cases vorhanden sind. Das> Resultat sind willkürlich leuchtende LEDs.
Da hast Du wohn noch ein paar andere Fehler in deinem Programm.
>> Die Erklärung warum es i <= NR_CASES - 1 lauten muss weiss ich noch> nicht. Die Struct sieht wie folgt aus:>
1
structLedCases[NR_CASES]=
2
>{
3
>{0x03,{0,5,5,0,5,0,0,5,0,0,0,0}},// case 1
4
>...
5
>{0x00,{0,0,0,0,0,0,0,0,0,0,0,0}},// case 18
6
>};
>> Vielleicht hat jemand eine Erklärung?
Ich wiederhole mich ja ungern, aber:
Georg Gast 1 schrieb:> Da in C/C++ Arrays nun einmal mit 0 beginnend indiziert werden ...
Noch mal im Klartext:
1
#define NR_CASES 18
2
3
structLed{wasauchimmer;};// Deklaration
4
5
structLedCases[NR_CASES]={
6
{0x03,{0,5,5,0,5,0,0,5,0,0,0,0}},// case 0 <-- das erste Element ist immer 0
7
...
8
{0x00,{0,0,0,0,0,0,0,0,0,0,0,0}},// case 17 <-- das letzte ist immer N-1
9
};
Auf
1
Cases[i]
darf nur mit i=0...17 zugegriffen werden. Alles andere ist ein Fehler.
Lesen außerhalb liefert beliebige Werte. Schreiben außerhalb macht
andere Variablen kaputt.
Vielleicht hast Du ähnliche Fehler ja noch anderswo im Programm.
Georg.
> struct Led Cases[NR_CASES] =
Hier musst Du selbst zählen und vergleichen, ob die Anzahl der
Initialisierungen mit (dem anderswo definierten) NR_CASES übereinstimmt.
Wenn Du Dein Array so anlegst:
> struct Led Cases[] =
kann der Compiler die Arraygröße an die Anzahl der angegebenen
Initialisierer anpassen.
Die wiederum kannst Du so bestimmen:
#define NR_CASES (sizeof Cases / sizeof Cases[0])
Das ist vielleicht etwas weniger fehlerträchtig, wenn Du irgendwann die
Anzahl der zu behandelnden Fälle verändern möchtest
Vielen Dank für den wertvollen Tipp und auch ein Dankeschön an alle die
geholfen haben. Es freut mich, dass man so ein Code debuggen und
optimieren kann und auch dabei lernt.
Gruss
Firebird
Ich mache das immer so, wenn ich etwas in einem Array suchen will:
1
for(i=0;i<N_CASES;i++)
2
{
3
if(array[i]==pattern)
4
{
5
break;// pattern found!
6
}
7
}
8
9
if(i==N_CASES)
10
{
11
// NOT found!
12
}
13
else
14
{
15
// found!
16
}
wobei N_CASES die Anzahl der tatsächlichen vorhandenen Array-Elemente
ist - also so, wie Rufus es exemplarisch vorgemacht hat. Ich finde es am
lesbarsten, wenn möglichst wenig Action in der for-Schleife selbst ist.
Frank M. schrieb:> Ich mache das immer so, wenn ich etwas in einem Array suchen will:
Ich habe so eine diffuse (vermutlich unbegründete) Angst, den
Schleifenzähler außerhalb zu verwerten. Z.B. weil ich die
Abbruchbedingung zweimal getrennt konsinstent schreiben muss.
Ich mach oft sowas:
1
{
2
/* Default value setzen */
3
intnextCase=LastCase;
4
5
for(i=0;i<NR_CASES;i++)
6
{
7
if(Cases[i].InputValue==data)
8
{
9
nextCase=i;
10
break;
11
}
12
}
13
caseNr=nextCase;
14
LastCase=nextCase;
15
}
Das hat aber noch mehr flaws:
A) eine zusätzliche Lokale Variable (vom Typ ... , auto wäre schön!)
B) eine doppelt gesetzte Variable (der zuerst zugewiesene Wert wird
nicht verwendet)
C) LastCase wird unconditional überschrieben, auch wenn nicht nowendig
Man könnte statt C) die Zuweisung vor's break packen, aber das
verwässert das eigentliche Ziel: die Trennung von Suche und Zuweisung.
auch mit Flag wird's nicht besser:
1
{
2
boolnot_found=TRUE;
3
4
for(i=0;i<NR_CASES;i++)
5
{
6
if(Cases[i].InputValue==data)
7
{
8
caseNr=i;
9
LastCase=i;
10
not_found=FALSE;
11
break;
12
}
13
}
14
if(not_found)
15
{
16
caseNr=LastCase;
17
}
18
}
Was ich sagen will: So ein else bei for, wie vom TO gewünscht, hätte
m.E. wirklich was.
Achim S. schrieb:> Ich habe so eine diffuse (vermutlich unbegründete) Angst, den> Schleifenzähler außerhalb zu verwerten.
Ja, Deine Angst ist unbegründet. Die for-Schleife
1
for(START;BEDINGUNG;WIEDERHOLUNG)
2
{
3
...
4
}
ist ja nur eine abkürzende Schreibweise für:
1
START;
2
while(BEDINGUNG)
3
{
4
...
5
WIEDERHOLUNG;
6
}
Die Schleifenvariable i hat daher denselben Stellenwert wie das Flag
not_found in Deinem unteren Beispiel.
Frank M. schrieb:> Ja, Deine Angst ist unbegründet. Die for-Schleife> ...> ist ja nur eine abkürzende Schreibweise für:
schon klar. Mir ging es darum, dass die Bedingung der Schleifen zweimal
leicht abgewandelt konsistent im Code stehen muss, während das flag
quasi nicht damit korreliert.
Frank M. schrieb:> Ja, Deine Angst ist unbegründet.
Jain. Wenn man bei for schleifen gerne
for (int i=0, i<MAXVALUE; i++) // bzw. besser
for (size_t i=0, i<MAXVALUE; i++)
schreibt, dann existiert i nur im "for" scope.
2⁵ schrieb:> Wenn man bei for schleifen gerne>> for (int i=0, i<MAXVALUE; i++) // bzw. besser> for (size_t i=0, i<MAXVALUE; i++)>> schreibt, dann existiert i nur im "for" scope.
Tja, wenn. Aber nur dann.
Ja, ich kenne die Argumente der Scope-Minimalisten. Aber es gibt auch
Argumente gegen die Definition innerhalb der for-Schleife. Zum Beispiel
versehentliches Shadowing oder auch die Nicht-Weiterverwendung außerhalb
der Schleife.
Ich sehe das als Geschmackssache an.
Firebird schrieb:> Ich habe das gleich probiert:> for (i = 0; i < NR_CASES; i++)> {> if (Cases[i].InputValue == data)> {> break; // pattern found!> }> }>> if (i == NR_CASES)> {> caseNr = LastCase; // NOT found!> }> else> {> caseNr = i; // found!> LastCase = caseNr;> }>
und ohne doppelten Vergleich auf NR_CASES, quasi im Vorbeigehen: ;-)
Gаst schrieb:> Durch solche Kniffe wird Quelltext in C aber sehr schnell genauso gut> lesbar wie Brainfuck oder gar Perl.
Ich weiss... Deshalb wünsche ich mir ja auch (wie der TO) ein else,
statt eines zusätzlichen ifs mit der negierten Bedingung. Ich finde es
immer blöd, das gleiche an 2 stellen zu pflegen, vor allem am Anfang und
nach dem Ende eines Blocks.
Es steht zwar nicht explizit in der Frage, aber ich denke schon, dass es
um C geht ... (in C++ würde man einfach einen generischen Algorithmus
benutzen)
Trotzdem kann man einfach das Iterator-Konzept auch in C anwenden.
Zeiger sind Iteratoren.
Firebird schrieb:> Ich habe das gleich probiert: for (i = 0; i < NR_CASES; i++)> {> if (Cases[i].InputValue == data)> {> break; // pattern found!> }> }>> if (i == NR_CASES)> {> caseNr = LastCase; // NOT found!> }> else> {> caseNr = i; // found!> LastCase = caseNr;> }
Man kann die Zuweisung für not found auch einfach vorziehen:
Zu den beiden letzten Lösungen hatte ich schon geschrieben, warum ich
sie ebenfalls nicht optimal finde:
Peter D. schrieb:> Man kann die Zuweisung für not found auch einfach vorziehen:Achim S. schrieb:> B) eine doppelt gesetzte Variable (der zuerst zugewiesene Wert wird> nicht verwendet)Wilhelm M. schrieb:> Somit ist last entweder ungültig oderAchim S. schrieb:> auch mit Flag wird's nicht besser:
Wenn jemand Lust und Zeit hat, kann er ja mal die verschiedenen
Varianten durch den gcc jagen. Ich würde wetten, dass die Variante mit
goto am besten abschneidet, weil dort keine doppelte Zuweisung und auch
kein doppelter Vergleich vorhanden ist. Ich kann mich natürlich
täuschen. Compiler sind sehr schlau heutzutage.
Gаst schrieb:> Wenn jemand Lust und Zeit hat,
Nee, weder Lust noch Zeit. Lohnt sich auch nicht, s.u.
> Ich würde wetten, dass die Variante mit> goto am besten abschneidet, weil dort keine doppelte Zuweisung und auch> kein doppelter Vergleich vorhanden ist.
Deine Formulierung "doppelt" hört sich erstmal nach einer Steigerung um
100% an. Das ist aber eine Milchmädchenrechnung. Tatsächlich erhöhen
sich Vergleiche bzw. Zuweisungen lediglich um 1.
Um das vernünftig zu messen, müsstest Du erstmal NR_CASES hinreichend
groß machen, z.B. auf 10000000. Dann ist der Unterschied von 10000000
auf 10000001 Zuweisungen bzw. Vergleiche im Verhältnis absolut
vernachlässigbar. Das geht im Rauschen unter.
> Compiler sind sehr schlau heutzutage.
Eben. Vermutlich kommt da spätestens ab -O2 derselbe oder adäquate Code
raus.