Forum: Compiler & IDEs for-Schleife mit else Anweisung


von Firebird (Gast)


Lesenswert?

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

von TM F. (p_richner)


Lesenswert?

Ich vermute wegen

i < NR_CASES - 1

dem -1

von Tassilo H. (tassilo_h)


Lesenswert?

Schreibst du doch auch so im for: NR_CASES - 1
Entweder i <= NR_CASES - 1 oder i < NR_CASES

von Gаst (Gast)


Lesenswert?

1
caseNr = lastCase;
2
for (i = 0; i < NR_CASES; i++) {
3
 if (Cases[i].InputValue == data) {
4
  caseNr = i;
5
  break;
6
 }
7
}

Evtl. so?

von CCC (Gast)


Lesenswert?

Gаst schrieb:
> caseNr = lastCase;
> for (i = 0; i < NR_CASES; i++) {
>  if (Cases[i].InputValue == data) {
>   caseNr = i;
>   break;
>  }
> }
> Evtl. so?

Plus Zuweisung lastCase
1
caseNr = lastCase;
2
for (i = 0; i < NR_CASES; i++) {
3
 if (Cases[i].InputValue == data) {
4
  caseNr   = i;
5
  lastCase = caseNr;
6
  break;
7
 }
8
}

von Joachim B. (jar)


Lesenswert?

case 0 ist aber vermutlich ungültig, deswegen inc't er ja vorher mit ++i 
nicht i++

mit for( i = 1; statt for( i = 0; wäre es aber auch gegangen

von Georg Gast 1 (Gast)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

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.

von Georg Gast 1 (Gast)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

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.

: Bearbeitet durch User
von Firebird (Gast)


Lesenswert?

Vielen Dank für eure Vorschläge. Ich habe gleich programmiert und 
getestet.

Mit
1
  i <= NR_CASES - 1
functionieren beide Varianten:
1
  for( i = 0; i <= NR_CASES-1; ++i )
2
  {
3
    if( Cases[i].InputValue == data )
4
    {
5
      caseNr = i;
6
      LastCase = caseNr;
7
      break;
8
    }
9
    else
10
    {
11
      caseNr = LastCase;
12
    }
13
  }
wie auch
1
  caseNr = LastCase;
2
    
3
  for( i = 0; i <= NR_CASES-1; ++i )
4
  {
5
    if( Cases[i].InputValue == data )
6
    {
7
      caseNr = i;
8
      LastCase = caseNr;
9
      break;
10
    }
11
  }
Mich irritiert die Tatsache, dass der Ursprüngliche Code mit
1
  i < NR_CASES - 1
funktioniert und bei der Variante mit der else Anweisung nicht.

von Joachim B. (jar)


Lesenswert?

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.

von Georg Gast 1 (Gast)


Lesenswert?

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.

von Joachim B. (jar)


Lesenswert?

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)
{
}

von Georg Gast 1 (Gast)


Lesenswert?

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.

von Georg Gast 1 (Gast)


Lesenswert?

Firebird schrieb:
> Mit  i <= NR_CASES - 1 functionieren beide Varianten

Warum schreibst Du nicht i < NR_CASES wie alle anderen?

von Joachim B. (jar)


Lesenswert?

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.

dann tuts auch nicht weh es zu ändern :)

welches C ist denn gemeint? K&R C89 C99?
https://de.wikipedia.org/wiki/Varianten_der_Programmiersprache_C

von Georg Gast 1 (Gast)


Lesenswert?

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.

von Firebird (Gast)


Lesenswert?

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
struct Led Cases[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
15
  { 0x09,  { 5, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0 } },             // case 13
16
  { 0x11,  { 0, 5, 0, 0, 0, 0, 0, 5, 0, 0, 5, 0 } },             // case 14
17
  { 0x21,  { 0, 5, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0 } },             // case 15
18
  { 0x41,  { 0, 0, 0, 0, 0, 0, 5, 0, 0, 5, 0, 5 } },             // case 16
19
  { 0x02,  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },             // case 17
20
  { 0x00,  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },             // case 18
21
};

Vielleicht hat jemand eine Erklärung?

von René H. (Gast)


Lesenswert?

Firebird schrieb:
> Mich irritiert die Tatsache, dass der Ursprüngliche Code mit

Weil Cases[NR_CASES] nicht existiert. Memory nicht initialisiert.

Grüsse,
René

von Georg Gast 1 (Gast)


Lesenswert?

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
 struct Led Cases[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
struct Led { wasauchimmer; }; // Deklaration
4
5
struct Led Cases[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.

von Firebird (Gast)


Lesenswert?

Die Led Struct ist wie folgt definiert:
1
struct Led
2
{
3
  unsigned char InputValue;           // Welcher Wert muss 'data' haben ...    
4
  unsigned char Values[PWM_CHANNELS]; // ... damit die Werte auf die Leds ausgegeben werden
5
};

Die Struct selber bleibt gleich, ich habe die 'case' Nummerierung 
korrigiert:
1
  
2
struct Led Cases[NR_CASES] =
3
{
4
  { 0x03,  { 0, 5, 5, 0, 5, 0, 0, 5, 0, 0, 0, 0 } },         // case 0
5
  ...
6
  { 0x00,  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },         // case 17
7
};
Ich habe jetzt die for Anweisung angepasst:
1
  // caseNr = NR_CASES - 1;             // definiert letzten Case als Default
2
  // for(i = 0; i < NR_CASES - 1; i++)  // Anweisung für letzten Case als Default
3
    
4
  caseNr = LastCase;                    // definiert den zuletzt gültigen Case
5
6
  for(i = 0; i < NR_CASES; i++)
7
  {
8
    if(Cases[i].InputValue == data)
9
    {
10
      caseNr = i;
11
      LastCase = caseNr;
12
      break;
13
    }
14
  }

Jetzt läuft das Programm durch ohne Fehler.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

> 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

von Firebird (Gast)


Lesenswert?

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

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von MaWin (Gast)


Lesenswert?

Frank M. schrieb:
> Ich mache das immer so, wenn ich etwas in einem Array suchen will:

In Python kann man auch dies machen:
1
for x in array:
2
  if x == pattern:
3
    # tue was
4
    break
5
else:
6
  # nicht gefunden

Das finde ich oft ganz nützlich.

von Firebird (Gast)


Lesenswert?

Frank M. schrieb:
> Ich mache das immer so, wenn ich etwas in einem Array suchen will:

Ich habe das gleich probiert:
1
  for (i = 0; i < NR_CASES; i++)
2
  {
3
    if (Cases[i].InputValue == data)
4
    {
5
      break;               // pattern found!
6
    }
7
  }
8
9
  if (i == NR_CASES)
10
  {
11
    caseNr = LastCase;     // NOT found!
12
  }
13
  else
14
  {
15
    caseNr = i;            // found!
16
    LastCase = caseNr;
17
  }

Ich vermute, dass die for Schleife schneller abgearbeitet wird.

von Eric B. (beric)


Lesenswert?

Firebird schrieb:
> Ich vermute, dass die for Schleife schneller abgearbeitet wird.

Eher nicht. Der Compiler optimiert das schon...

von A. S. (Gast)


Lesenswert?

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
int nextCase = 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
bool not_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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
von A. S. (Gast)


Lesenswert?

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.

von 2⁵ (Gast)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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: ;-)
1
for (i = 0; i < NR_CASES||(caseNr=LastCase,0); i++)
2
{
3
  if (Cases[i].InputValue == data)
4
  {
5
    caseNr = i;            // found!
6
    LastCase = caseNr;
7
    break; 
8
  }
9
}

von Gаst (Gast)


Lesenswert?

Durch solche Kniffe wird Quelltext in C aber sehr schnell genauso gut 
lesbar wie Brainfuck oder gar Perl.

von A. S. (Gast)


Lesenswert?

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.

von Gаst (Gast)


Lesenswert?

Wenn ich es optimieren und lesbar schreiben müsste (was ich auch 
freiwillig tun würde), würde ich es so probieren:
1
for (size_t i = 0; i < NR_CASES; i++) {
2
 if (Cases[i].InputValue == data) {
3
  caseNr = i;
4
  LastCase = caseNr;
5
  goto found;
6
 }
7
}
8
9
// NOT found!
10
caseNr = LastCase;
11
12
found:
13
// ...

Aber goto ist ja böse und des Teufels. Ahhhhhh, wir brauchen einen 
Exorzisten...

von Wilhelm M. (wimalopaan)


Lesenswert?

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.
1
    const int* last = NULL;
2
    for(const int* it = &array[0]; it < &array[NCASES]; ++it) {
3
        if (*it == value) {
4
            last = it;
5
        }
6
    }
7
8
    if(last) {
9
        // *last
10
    }

Somit ist last entweder ungültig oder verweist auf das letzte gefundene 
Element.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

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:
1
  caseNr = LastCase;     // if NOT found!
2
  for (i = 0; i < NR_CASES; i++)
3
  {
4
    if (Cases[i].InputValue == data)
5
    {
6
      caseNr = i;            // found!
7
      LastCase = caseNr;
8
      break;               // pattern found!
9
    }
10
  }

von A. S. (Gast)


Lesenswert?

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 oder

Achim S. schrieb:
> auch mit Flag wird's nicht besser:

von Gаst (Gast)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

: Bearbeitet durch Moderator
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.