Forum: Compiler & IDEs C: try this, then this, then this, then this, then this.


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

ich benötige gerade ein Konstrukt der Form: Probier das, dann das, dann 
das usw. und wenn das letzte fehlschlägt, erzeuge einen Fehler.

In Quelltext sieht das momentan so aus:
1
int foo(int prm, char c)
2
{
3
    int ret = tryToDoWhatIWantYouToDo(c);
4
5
    if( ret == EXIT_SUCCESS )
6
    {
7
        continueWithTheStuffYouStarted(prm);
8
        return EXIT_SUCCESS;
9
    }
10
    else
11
    {
12
        // Zeichen nicht vorhanden -> Ersatzzeichen
13
        ret = foo(prm, '@');
14
        if( ret == EXIT_FAILURE )
15
        {
16
            ret = foo(prm, '#');
17
            if( ret == EXIT_FAILURE )
18
            {
19
                ret = foo(prm, 'A');
20
                if( ret == EXIT_FAILURE )
21
                {
22
                    ret = foo(prm, '0');
23
                    if( ret == EXIT_FAILURE )
24
                    {
25
                        error("Invalid character")
26
                    }
27
                }
28
            }
29
        }
30
        return EXIT_FAILURE;
31
    }
32
}

Wenn ich jetzt noch ein paar Sachen ausprobiere, bevor ich den Fehler 
ausgebe, sieht das am Ende so aus, wie eine schriftliche Division in der 
Grundschule.

Mir fallen ein paar Möglichkeiten ein, wie ich das weniger länglich 
schreiben könnte. Aber vorher interessiert mich:

Gibt es ein Standard-Design-Pattern für diese Art von Problemstellung?

Viele Grüße
W.T.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

1
        if ((ret = foo(prm, '@')) == EXIT_FAILURE &&
2
            (ret = foo(prm, '#')) == EXIT_FAILURE &&
3
            (ret = foo(prm, 'A')) == EXIT_FAILURE &&
4
            (ret = foo(prm, '0')) == EXIT_FAILURE)
5
        {
6
            error("Invalid character")
7
        }

Übrigens fehlt am Ende der Funktion noch ein "return ret;" ;-)

: Bearbeitet durch Moderator
von (prx) A. K. (prx)


Lesenswert?

Technisch gleich, optisch ansprechender:

    if (tryToDoWhatIWantYouToDo(c) == EXIT_SUCCESS )
    {
        continueWithTheStuffYouStarted(prm);
        return EXIT_SUCCESS;
    }
    else if (foo(prm, '@') == EXIT_FAILURE
          && foo(prm, '#') == EXIT_FAILURE
          && ... )
    {
        error("Invalid character")
    }

: Bearbeitet durch User
von J. F. (Firma: Père Lachaise) (rect)


Lesenswert?

Einfach eine logische Verknüpfung? Ich weiß nicht warum du hier ein 
Design Pattern einsetzen möchtest. Oder verstehe ich dein Anliegen 
nicht?

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Gibt es ein Standard-Design-Pattern für diese Art von Problemstellung?

Siehe oben: Entweder verkette ich das im if() mit && oder mit || - je 
nachdem, wie die Logik ist.

Dann wird das Ganze nicht so länglich und ist sogar noch lesbarer als 
die multiplen if-Statements.

Beitrag #5342018 wurde vom Autor gelöscht.
von A. (Gast)


Lesenswert?

Wenn es noch mehr werden:
1
char * zeichen = "@#A0";
2
3
for(uint8_t i = 0; i < strlen(zeichen); i++){
4
  if(foo(prm, zeichen[i]) != EXIT_FAILURE){
5
    return EXIT_SUCCESS;
6
  }
7
}
8
9
return EXIT_FAILURE;

von (prx) A. K. (prx)


Lesenswert?

Ach ja, leicht zu übersehen. Die Sache rekursiv abzuwickeln ist 
vielleicht nicht die beste Idee.

von (prx) A. K. (prx)


Lesenswert?

Frank M. schrieb im Beitrag #5342018:
> am Ende der Funktion vergessen hat.

Ich hatte mich exakt an sein Vorbild gehalten, das fehlende Semikolon 
inklusive. Aber das Hauptproblem ist die Rekursion, und die hast du auch 
übersehen. ;-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

A. K. schrieb:
> Ich hatte mich exakt an sein Vorbild gehalten, das fehlende Semikolon
> inklusive. Aber das Hauptproblem ist die Rekursion, und die hast du auch
> übersehen. ;-)

Upps, die hatte ich tatsächlich übersehen. Und nicht nur das, auch das 
return im else-Zweig fiel mir nicht auf. Damit kann die Zuweisung "ret = 
foo(...)" tatsächlich entfallen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Die Rekursion kann man eigentlich ziemlich einfach plattmachen:
1
static int bar(int prm, char c)
2
{
3
    int ret = tryToDoWhatIWantYouToDo(c);
4
5
    if( ret == EXIT_SUCCESS )
6
    {
7
        continueWithTheStuffYouStarted(prm);
8
        return EXIT_SUCCESS;
9
    }
10
    return EXIT_FAILURE;
11
}
12
13
int foo (int prm, char c)
14
{
15
    if (bar(prm, c) == EXIT_FAILURE)
16
    {
17
        if (bar(prm, '@') == EXIT_FAILURE &&
18
            bar(prm, '#') == EXIT_FAILURE &&
19
            bar(prm, 'A') == EXIT_FAILURE &&
20
            bar(prm, '0') == EXIT_FAILURE)
21
        {
22
            error("Invalid character")
23
        }
24
        return EXIT_FAILURE;
25
    }
26
    return EXIT_SUCCESS;
27
}

Oder habe ich etwas übersehen? Ich verstehe auch nicht, warum auch bei 
erfolgreich gefundenem Ersatzzeichen ein EXIT_FAILURE zurückgegeben 
werden soll.

EDIT:
Ein # durch @ ersetzt.

: Bearbeitet durch Moderator
von Walter S. (avatar)


Lesenswert?

Rekursion kann durchaus elegant sein, aber in dem Beispiel trägt sie nur 
nur Verwirrung bei, also weg damit

von Walter T. (nicolas)


Lesenswert?

Danke für die Antworten.

Ja - das Semikolon habe ich vergessen.


Rekursion: Stimmt. Das könnte zum Problem werden.

Die Funktion ist - wie so oft - ein gewachsenes Konstrukt. Ursprünglich 
wurde maximal ein Ersatzzeichen ausprobiert, womit die Rekursionstiefe 
auf 1 begrenzt war. Mit wachsender Anzahl an Ersatzzeichen ist das 
eigentlich nicht mehr tragbar. Ich werde das Design also komplett 
ändern, wodurch die urspruengliche Fragestellung entfällt.

Frank M. schrieb:
> warum auch bei
> erfolgreich gefundenem Ersatzzeichen ein EXIT_FAILURE zurückgegeben
> werden soll.

Ein Ersatzzeichen ist eben nicht das, was die Funktion ursprünglich mal 
bearbeiten sollte.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Rekursion: Stimmt. Das könnte zum Problem werden.

Dann mach es so wie oben dargestellt mit bar() und foo(). Dann gibts 
keine Rekursion mehr.

> Ein Ersatzzeichen ist eben nicht das, was die Funktion ursprünglich mal
> bearbeiten sollte.

Okay :-)

von Walter T. (nicolas)


Lesenswert?

So sieht die Funktion jetzt nach dem "Refactoring" aus:
1
int foo2(int prm, char c)
2
{
3
    int ret = tryToDoWhatIWantYouToDo(c);
4
5
    if( ret == EXIT_FAILURE)
6
    {
7
        if( tryToDoWhatIWantYouToDo('@') == EXIT_SUCCESS );
8
        else if( tryToDoWhatIWantYouToDo('#') == EXIT_SUCCESS );
9
        else if( tryToDoWhatIWantYouToDo('A') == EXIT_SUCCESS );
10
        else if( tryToDoWhatIWantYouToDo('0') == EXIT_SUCCESS );
11
        else
12
        {
13
            error("Invalid character");
14
        }
15
    }
16
17
    continueWithTheStuffYouStarted(prm);
18
    return ret;
19
}

von Kerl (Gast)


Lesenswert?

Was spricht gegen SWITCH CASE?

von (prx) A. K. (prx)


Lesenswert?

Kerl schrieb:
> Was spricht gegen SWITCH CASE?

Der Funktionsaufruf.

von (prx) A. K. (prx)


Lesenswert?

Walter T. schrieb:
> So sieht die Funktion jetzt nach dem "Refactoring" aus:

Leidest du unter einer Operatoren-Allergie?
In dem Fall empfehle ich COBOL statt C. ;-)

Dann wenigstens optisch so:
        if( tryToDoWhatIWantYouToDo('@') == EXIT_SUCCESS )
            ;
        else if( tryToDoWhatIWantYouToDo('#') == EXIT_SUCCESS )
            ;
        else if( tryToDoWhatIWantYouToDo('A') == EXIT_SUCCESS )
Damit das Semikolon nicht so falsch aussieht.

: Bearbeitet durch User
von Sebastian Michel (Gast)


Lesenswert?

Das müsste auch funktionieren:
1
        // Zeichen nicht vorhanden -> Ersatzzeichen
2
        ret = foo(prm, '@');
3
        if( ret == EXIT_FAILURE )
4
        {
5
            ret = foo(prm, '#');
6
        }
7
8
        if( ret == EXIT_FAILURE )
9
        {
10
            ret = foo(prm, 'A');
11
        }
12
13
        if( ret == EXIT_FAILURE )
14
        {
15
            ret = foo(prm, '0');
16
        }
17
18
        if( ret == EXIT_FAILURE )
19
        {
20
            error("Invalid character")
21
        }

von Walter T. (nicolas)


Lesenswert?

A. K. schrieb:
> Leidest du unter einer Operatoren-Allergie?

Nein. Wohin gehört Deiner Meinung nach einer?

A. K. schrieb:
> In dem Fall empfehle ich COBOL statt C.
Gibt es nicht für meine Zielplattform. Fortran auch nicht.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Walter T. schrieb:
>> Leidest du unter einer Operatoren-Allergie?
>
> Nein. Wohin gehört Deiner Meinung nach einer?

   if( tryToDoWhatIWantYouToDo('@') != EXIT_SUCCESS
    && tryToDoWhatIWantYouToDo('#') != EXIT_SUCCESS
    && tryToDoWhatIWantYouToDo('A') != EXIT_SUCCESS
    && tryToDoWhatIWantYouToDo('0') != EXIT_SUCCESS)
        error("Invalid character");

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Walter T. schrieb:
> Nein. Wohin gehört Deiner Meinung nach einer?

A.K. meinte das Semikolin am Ende der if-Zeilen. Das sieht so einfach 
"aus Versehen" aus. Nach ein paar Monaten kommst Du dann auch nochmal 
ins Grübeln, ob das jetzt falsch ist oder nicht.

Ich schreibe bei so etwas immer:
1
     if (....)
2
     {
3
         ;
4
     }

um herauszustellen, dass das Semikolon Absicht ist. Deshalb meinte 
auch A.K., dass Du es zumindest auf eine extra Zeile setzen solltest.

von Walter T. (nicolas)


Lesenswert?

A. K. schrieb:
> if( tryToDoWhatIWantYouToDo('@') != EXIT_SUCCESS
>     && tryToDoWhatIWantYouToDo('#') != EXIT_SUCCESS
>     && tryToDoWhatIWantYouToDo('A') != EXIT_SUCCESS
>     && tryToDoWhatIWantYouToDo('0') != EXIT_SUCCESS)
>         error("Invalid character");

In der Präprozessor-Sprache hätte ich es so geschrieben.

In C fällt das für mich unter die Rubrik "Geschmacksache".

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

A. K. schrieb:
> if( tryToDoWhatIWantYouToDo('@') != EXIT_SUCCESS
>     && tryToDoWhatIWantYouToDo('#') != EXIT_SUCCESS
>     && tryToDoWhatIWantYouToDo('A') != EXIT_SUCCESS
>     && tryToDoWhatIWantYouToDo('0') != EXIT_SUCCESS)
>         error("Invalid character");

Offenbar sehen wir das genauso :-)

von A. S. (Gast)


Lesenswert?

A. schrieb:
> Wenn es noch mehr werden:
> char * zeichen = "@#A0";
>
> for(uint8_t i = 0; i < strlen(zeichen); i++) ...

Das sollte doch m.E. die Grundlage sein, abgesehen vom falschen 
Rückgabewert.

Ich gehe zudem davon aus, dass die Ersatzzeichen nicht bis zu 4 Mal 
ausprobiert werden sollten, sondern nur einmal. Dann ergäbe sich z.B.:
1
int foo(int prm, char c)
2
{
3
char *p = "@#A0";
4
int ret = EXIT_SUCESS;
5
6
    do
7
    {
8
        if(tryToDoWhatIWantYouToDo(c) == EXIT_SUCCESS ) 
9
        {
10
            continueWithTheStuffYouStarted(prm);
11
            return ret;
12
        }
13
        c=*p++;
14
        ret = EXIT_FAILURE;        
15
    }while(c);
16
    
17
    error("Invalid character")
18
    return EXIT_FAILURE;
19
}

von Peter D. (peda)


Lesenswert?

1
  do{
2
    if( a ) break;
3
    if( b ) break;
4
    if( c ) break;
5
    if( d ) break;
6
    do_something();
7
  }while(0);

von A. S. (Gast)


Lesenswert?

Peter D. schrieb:
>
1
>   do{
2
>     if( a ) break;
3
>     if( b ) break;
4
>     if( c ) break;
5
>     if( d ) break;
6
>     do_something();
7
>   }while(0);
8
>

Ausrollen UND do while? Der Orignalcode sieht refaktoriert und 
ausgerollt doch so aus:
1
int foo(int prm, char c)
2
{
3
   if(tryToDoWhatIWantYouToDo(c)== EXIT_SUCCESS )
4
   {
5
      continueWithTheStuffYouStarted(prm);
6
      return EXIT_SUCCESS;
7
   }
8
   if( foo(prm, '@') != EXIT_FAILURE ) {return EXIT_FAILURE;}
9
   if( foo(prm, '#') != EXIT_FAILURE ) {return EXIT_FAILURE;}
10
   if( foo(prm, 'A') != EXIT_FAILURE ) {return EXIT_FAILURE;}
11
   if( foo(prm, '0') != EXIT_FAILURE ) {return EXIT_FAILURE;}
12
   error("Invalid character")
13
   return EXIT_FAILURE;
14
}

von Nop (Gast)


Lesenswert?

Achim S. schrieb:

> Ausrollen UND do while?

Das ist die Lösung für Leute, die eigentlich ein "goto" zur 
Fehlerbehandlung schreiben wollen, sich aber nicht trauen, dazu offen zu 
stehen.

von Jack (Gast)


Lesenswert?

1
int fail() {
2
    error("Invalid character");
3
    return EXIT_FAILURE;
4
}
5
6
int foo(int prm, char c) {
7
    char chars[] = { c, '@', '#', 'A', '0' };
8
    for(int i = 0; i < sizeof(chars); i++) {
9
        if(tryToDoWhatIWantYouToDo(chars[i]) == EXIT_SUCCESS) {
10
            continueWithTheStuffYouStarted(prm);
11
            return i ? fail() : EXIT_SUCCESS;
12
    }
13
    return fail();
14
}

von Jan (Gast)


Lesenswert?

>Achim S. schrieb:

>> Ausrollen UND do while?

>Das ist die Lösung für Leute, die eigentlich ein "goto" zur
>Fehlerbehandlung schreiben wollen, sich aber nicht trauen, dazu offen zu
>stehen.

Diese do {} while(0); mit breaks - Lösung von Peter Dannegger finde ich 
am besten lesbar vom all dem was hier bis jetzt zu sehen war.
Ein mal habe ich genau so ein Konstrukt nach mehreren hin und her auch 
verwendet. ( do {} while(0); mit mehreren breaks. )

Dabei musste ich auch an goto denken. Trotzdem habe ich mich dafür 
entschieden. Ist es wirklich zu sehen wie ein goto? oder kann man es 
doch ausnahmsweise verwenden?

Gruß, Jan

von dummschwaetzer (Gast)


Lesenswert?

Jan schrieb:
> Ist es wirklich zu sehen wie ein goto? oder kann man es
> doch ausnahmsweise verwenden?

mann darf auch goto verwenden

von Rolf M. (rmagnus)


Lesenswert?

Jan schrieb:
> Dabei musste ich auch an goto denken. Trotzdem habe ich mich dafür
> entschieden. Ist es wirklich zu sehen wie ein goto?

Ich finde es schlechter als goto.

von Jan (Gast)


Lesenswert?

Danke für Eure Meinungen,

 > Ich finde es schlechter als goto

Ich sehe es nicht so kritisch. Oft findet man Ähnliches, wenn ein Array 
durchsucht wird.Dort geht man auch mit einem break aus der Schleife 
raus, nach dem man das gesuchte Element gefunden hat.


Wenn die Schleife, die man mit break unterbricht, kurz und übersichtlich 
ist, finde ich es in Ordnung mit break zu unterbrechen. Alles andere 
führt dazu, das es aussieht wie eine "schriftliche Division in der
 Grundschule", oder es endet mit Tausenden klammern und else-Ausdrücken.

Warum hat man den break eingeführt? Nur für die switch case Anweisung?
Es verhält sich halt wie ein goto.
Ähnliches ist es mit continue.

Gruss,
Jan

von Eric B. (beric)


Lesenswert?

oder so:
1
int foo_mit_ersatz(int prm, char c, char *ersatz)
2
{
3
  int ret = tryToDoWhatYouWant(prm, c);
4
5
  if((ersatz != NULL)
6
  {
7
    for(char *pc = ersatz;(*pc != 0) && (ret == EXIT_FAILURE); pc++)
8
    {
9
      ret = tryToDoWhatYouWant(prm, *pc);
10
    }
11
  }
12
  return ret;
13
}
14
15
int foo(int prm, char c)
16
{
17
  return foo_mit_ersatz(prm, c, "@#A0");
18
}

von Jan (Gast)


Lesenswert?

Gerade in so einem Fall, wo es an mehreren Stellen zu einem Abbruch des 
Verarbeitungsfluss kommen kann und dann immer das selbe getan werden 
soll, dann drängt sich das goto doch quasi auf.
Leider ist das goto zu unrecht verpönt, aber das leigt eigentlich nur 
daran, dass es gerade Anfänger einlädt schmu zu Treiben. Aber zum 
Abbrechen von Abläufen oder mehrdimensionalen Iterationen ist es meiner 
Meinung nach die lesbarste und flexibelste Lösung. Solche Konstrukte 
habe ich sogar schon in Linux Kerneltreibern gesehen...

Jan

1
int foo(int prm, char c)
2
{
3
    int ret = ;
4
5
    if( tryToDoWhatIWantYouToDo(c) == EXIT_SUCCESS )
6
    {
7
        continueWithTheStuffYouStarted(prm);
8
        return EXIT_SUCCESS;
9
    }
10
    else
11
    {
12
    if( foo(prm, '@') == EXIT_FAILURE ) goto failure_exit;
13
    if( foo(prm, '#') == EXIT_FAILURE ) goto failure_exit;
14
    if( foo(prm, 'A') == EXIT_FAILURE ) goto failure_exit;
15
    if( foo(prm, '0') == EXIT_FAILURE ) goto failure_exit;
16
    
17
    //It worked, so return success
18
    return EXIT_SUCCESS;
19
  
20
    //Something failed, lets report it
21
  failure_exit:
22
    error("Invalid character");
23
    return EXIT_FAILURE;
24
    }
25
}

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Jan schrieb:
> Solche Konstrukte
> habe ich sogar schon in Linux Kerneltreibern gesehen...

warum "sogar" ?

von Jan (Gast)


Lesenswert?

Das sogar richtete sich eher an die Verteufelung und die allgemeinen 
Ratschläge, dass das goto eine Ausgeburt des Teufels ist und unter gar 
keinen Umständen verwendet werden darf, Michael ;-)

von Jan (Gast)


Lesenswert?

Die letzten zwei Posts vom Autor Jan (Gast) sind nicht von mir.
10:22 und 10:25

(Mit mir meine ich, der Jan der sich hier zu diesem Thema das erste mal 
geäußert hat)

Ich werde mir ein Account anlegen und nicht mehr anonym schreiben.
Gruss,
Jan

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Jan schrieb:
> goto eine Ausgeburt des Teufels ist

Ach, du meinst das mit den sieben Reitern der Apokalypse und so ;-)

von (prx) A. K. (prx)


Lesenswert?

Michael R. schrieb:
>> goto eine Ausgeburt des Teufels ist
>
> Ach, du meinst das mit den sieben Reitern der Apokalypse und so ;-)

Das geht hierauf zurück, von 1968, und bezog sich wesentlich auf einen 
Programmierstil wie in unstrukturiertem Fortran:
"Edgar Dijkstra: Go To Statement Considered Harmful"
http://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF

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


Lesenswert?

Jan schrieb:
> Gerade in so einem Fall, wo es an mehreren Stellen zu einem Abbruch des
> Verarbeitungsfluss kommen kann und dann immer das selbe getan werden
> soll, dann drängt sich das goto doch quasi auf.

Ja, aber in der Aufgabe hier ist der Sprung unnötig, da reicht return ( 
siehe oben Beitrag "Re: C: try this, then this, then this, then this, then this.")

Wäre ein goto sinnvoll, wäre es mir egal, ob goto oder do while.

von Walter T. (nicolas)


Lesenswert?

Peter D. schrieb:
> do{
>     if( a ) break;
>     if( b ) break;
>     if( c ) break;
>     if( d ) break;
>     do_something();
>   }while(0);



A. K. schrieb:
> if( tryToDoWhatIWantYouToDo('@') != EXIT_SUCCESS
>     && tryToDoWhatIWantYouToDo('#') != EXIT_SUCCESS
>     && tryToDoWhatIWantYouToDo('A') != EXIT_SUCCESS
>     && tryToDoWhatIWantYouToDo('0') != EXIT_SUCCESS)
>         error("Invalid character");


Haben diese beiden Entwurfsmuster auch (jeweils) einen Namen?

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Jan schrieb:
> Danke für Eure Meinungen,
>
>  > Ich finde es schlechter als goto
>
> Ich sehe es nicht so kritisch. Oft findet man Ähnliches, wenn ein Array
> durchsucht wird.Dort geht man auch mit einem break aus der Schleife
> raus, nach dem man das gesuchte Element gefunden hat.

Ja, aber da ist es dann auch eine richtige Schleife und nicht nur ein 
Pseudo-Schleifenkonstrukt, der nur dazu dient, break verwenden zu 
können.
Mich stört nicht die Verwendung von break, sondern die von while für 
etwas, das gar keine Schleife ist.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Rolf M. schrieb:
> Mich stört nicht die Verwendung von break, sondern die von while für
> etwas, das gar keine Schleife ist.

Das Problem lässt sich leicht lösen:

1
  switch(0) { default:
2
    if (a) break;
3
    if (b) break;
4
    if (c) break;
5
    if (d) break;
6
    do_something();
7
  }

;-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Das Problem lässt sich leicht lösen:

Der war gut! Nach dem Motto: Wie schreibe ich 'goto' anders? :-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Falls es auch C++ sein darf (ok, im Subject steht C, trotzdem gehe ich 
mal davon aus, dass man das betreffende Modul auch mit einem 
C++-Compiler übersetzen kann):
1
[]{
2
if (a) return;
3
if (b) return;
4
if (c) return;
5
if (d) return;
6
doSomething();
7
}();

von Bernd K. (prof7bit)


Lesenswert?

Michael R. schrieb:
> Jan schrieb:
> Solche Konstrukte
> habe ich sogar schon in Linux Kerneltreibern gesehen...
>
> warum "sogar" ?

Weil dort keine Anfänger coden sondern Leute auf Guru-Level, solche die 
Treiberberquelltext beim Frühstück lesen  statt der Zeitung.

von Jan W. (jan_woj)


Lesenswert?

Rolf Magnus schrieb :
>Ja, aber da ist es dann auch eine richtige Schleife und nicht nur ein
>Pseudo-Schleifenkonstrukt, der nur dazu dient, break verwenden zu
>können.
>Mich stört nicht die Verwendung von break, sondern die von while für
>etwas, das gar keine Schleife ist.

Dem muss ich zustimmen. Eine do while(0); ist keine schleife. Eigentlich 
missbraucht man an dieser Stelle die Sprache, es ist Irreführend für den 
Leser. Man könnte vielleicht sogar eine Compilerwarnung an dieser Stelle 
erwarten.

Mit goto wäre es sauberer an dieser Stelle. Da goto aber so verpönt ist, 
benutzt es kaum jemand.
Dafür neigt man lieber zum Missbrauch von  Schleifen.

Gruss,
Jan

von Karl (Gast)


Lesenswert?

Bernd K. schrieb:
> Weil dort keine Anfänger coden sondern Leute auf Guru-Level, solche die
> Treiberberquelltext beim Frühstück lesen  statt der Zeitung.

Muhaha!

http://cdn3.spiegel.de/images/image-662536-galleryV9-erjl-662536.jpg

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Karl schrieb:
> Muhaha!
>
> http://cdn3.spiegel.de/images/image-662536-galleryV9-erjl-662536.jpg

Nett :-)

Aus diesem Grunde benutze ich grundsätzlich unterhalb jeder 
Kontrollstruktur wie if, while, for usw. geschweifte Klammern. Die 
beiden gotos innerhalb dieser Klammern hätten dann zu einer 
Compiler-Warnung geführt und nicht zu einem Laufzeitfehler.

: Bearbeitet durch Moderator
von (prx) A. K. (prx)


Lesenswert?

Frank M. schrieb:
> Die beiden gotos innerhalb dieser Klammern hätten dann zu einer
> Compiler-Warnung geführt

Das if hinter dem zweiten goto sollte eigentlich eine solche 
"unreachable code" Warnung auslösen.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

A. K. schrieb:
> Das if hinter dem zweiten goto sollte eigentlich eine solche
> "unreachable code" Warnung auslösen.

Stimmt auch wieder.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

1
$ cat goto.c
1
#include <stdio.h>
2
3
int main ()
4
{
5
    printf ("Hello, ");
6
    goto a;
7
    goto a;
8
    printf ("world\n");
9
a:
10
    return 0;
11
}
1
$ gcc -O goto.c
2
$ gcc -O -Wall goto.c
3
$ gcc -O -Wall -Wextra goto.c
4
$ gcc -O -Wall -Wextra -Wunreachable-code goto.c

Warum kommt hier keine Warnung?

gcc version 6.3.0 20170516

: Bearbeitet durch Moderator
von Walter K. (Gast)


Lesenswert?

Jan schrieb:
> Leider ist das goto zu unrecht verpönt, aber das leigt eigentlich nur
> daran, dass es gerade Anfänger einlädt schmu zu Treiben. Aber zum
> Abbrechen von Abläufen oder mehrdimensionalen Iterationen ist es meiner
> Meinung nach die lesbarste und flexibelste Lösung. Solche Konstrukte
> habe ich sogar schon in Linux Kerneltreibern gesehen...
>
....
>     {
>     if( foo(prm, '@') == EXIT_FAILURE ) goto failure_exit;
>     if( foo(prm, '#') == EXIT_FAILURE ) goto failure_exit;
>     if( foo(prm, 'A') == EXIT_FAILURE ) goto failure_exit;
>     if( foo(prm, '0') == EXIT_FAILURE ) goto failure_exit;
>
>     //It worked, so return success
>     return EXIT_SUCCESS;
>
>     //Something failed, lets report it
>   failure_exit:
>     error("Invalid character");
>     return EXIT_FAILURE;
>     }

Wenn Anfänger solchen Unsinn lesen und sowas verinnerlichen, weil sie 
zwischen guten Ratschlägen und Tipps zum Murx noch nichts unterscheiden 
können ...
finde ich ziemlich schlimm!

4 x mit goto zum gleichen return springen ..und vorher immer die gleiche 
Funktion error(blah_blah) aufrufen - um damit die Sinnhaftigkeit von 
goto in C zu demonstrieren ..... ohne Worte!!!

von (prx) A. K. (prx)


Lesenswert?

"The -Wunreachable-code has been removed, because it was unstable:"
https://gcc.gnu.org/ml/gcc-help/2011-05/msg00360.html

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

A. K. schrieb:
> "The -Wunreachable-code has been removed, because it was unstable:"

Weia. Und das schon seit 2011! Mit so etwas hätte ich jetzt nicht 
gerechnet.
1
$ clang -O -Wall -Wextra -Wunreachable-code goto.c
2
goto.c:7:5: warning: code will never be executed [-Wunreachable-code]
3
    printf ("world\n");
4
    ^~~~~~

Ich werde mich jetzt mal mit clang beschäftigen...

von Karl (Gast)


Lesenswert?

A. K. schrieb:
> Das if hinter dem zweiten goto sollte eigentlich eine solche
> "unreachable code" Warnung auslösen.

Wer weiss wie viele Warnungen da noch drin sind. Die ignoriert man halt, 
hauptsache es kompiliert.

Ich hab auch 2 unreachable code-Warnungen in meinem aktuellen Projekt. 
Ich inkrementiere einen uint8 und wenn der Buffermax erreicht 
(Bufferlänge) setze ich ihn zurück. Nun ist Buffermax = 256, wird also 
nie erreicht bzw. automatisch wieder Null. Trotzdem lasse ich die 
Abfrage drin, weil ich ja mal die Bufferlänge verringern könnte und dann 
ist die wichtig. Bekomme also immer Compilerwarnungen, hab aber auch 
keine andere Lösung dafür. (Nein, uint16 verwenden ist keine Lösung ;-))

von Wilhelm M. (wimalopaan)


Lesenswert?

Wilhelm M. schrieb:
> Falls es auch C++ sein darf (ok, im Subject steht C, trotzdem gehe ich
> mal davon aus, dass man das betreffende Modul auch mit einem
> C++-Compiler übersetzen kann):
>
>
1
> []{
2
> if (a) return;
3
> if (b) return;
4
> if (c) return;
5
> if (d) return;
6
> doSomething();
7
> }();
8
>

Gerade noch eingefallen ...
1
    [](auto... cc) {
2
         (f(cc) && ... && doSth());
3
    }(c, '@', '$');

In der Kürze liegt die Würze.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Bernd K. schrieb:
> Michael R. schrieb:
>> Jan schrieb:
>> Solche Konstrukte
>> habe ich sogar schon in Linux Kerneltreibern gesehen...
>>
>> warum "sogar" ?
>
> Weil dort keine Anfänger coden sondern Leute auf Guru-Level, solche die
> Treiberberquelltext beim Frühstück lesen  statt der Zeitung.

Karl schrieb:
> Muhaha!
>
> http://cdn3.spiegel.de/images/image-662536-galleryV9-erjl-662536.jpg

Dieser Link hat aber weder mit Linux noch mit Kerneltreibern etwas zu
tun, sondern zeigt allenfalls, dass bei Apple die Codequalität seit
Steve Wozniaks Rückzug etwas nachgelassen hat ;-)

von Bernd K. (prof7bit)


Lesenswert?

Walter K. schrieb:
> x mit goto zum gleichen return springen

Walter K. schrieb:
> 4 x mit goto zum gleichen return springen

Er springt nicht zum return. Sonst hätt er gleich return hinschreiben 
können.

von Walter K. (Gast)


Lesenswert?

Bernd K. schrieb:
> Walter K. schrieb:
>> 4 x mit goto zum gleichen return springen
>
> Er springt nicht zum return. Sonst hätt er gleich return hinschreiben
> können.

Lesekompetenz?

wenn Du meinen Satz komplett gelesen hättest....

von Bernd K. (prof7bit)


Lesenswert?

Walter K. schrieb:
>> Er springt nicht zum return. Sonst hätt er gleich return hinschreiben
>> können.
>
> Lesekompetenz?
>
> wenn Du meinen Satz komplett gelesen hättest....

Schreibkompetenz?

Du hast geschrieben daß er vorher eine Funktion error() ausführt und 
danach mit goto zum return springt. Das ist nicht der Fall, er springt 
nicht mit goto zum return und error() wird auch nicht vor dem goto 
aufgerufen. Dein Geschreibsel war völlig losgelöst von der Realität, 
insgesamt inkoheränt und am Ende hast Du dann auch noch vergessen zu 
schreiben was Du eigentlich sagen wolltest, es endet einfach abrupt ohne 
zur Kernaussage gelangt zu sein. Wenn Du irgendetwas sagen wolltest dann 
lerne bitte Dich vernünftig zu artikulieren.

: Bearbeitet durch User
von Walter K. (Gast)


Lesenswert?

Bernd K. schrieb:
> Du hast geschrieben daß er vorher eine Funktion error() ausführt und
> danach mit goto zum return springt. Das ist nicht der Fall, er springt
> nicht mit goto zum return und error() wird auch nicht vor dem goto
> aufgerufen. Dein Geschreibsel war völlig losgelöst von der Realität,
> insgesamt inkoheränt und am Ende hast Du dann auch noch vergessen zu
> schreiben was Du eigentlich sagen wolltest, es endet einfach abrupt ohne
> zur Kernaussage gelangt zu sein. Wenn Du irgendetwas sagen wolltest dann
> lerne bitte Dich vernünftig zu artikulieren.

Mein Gott ... anstatt Dich hier aufzublasen, von Kohärenzen zu 
schwadronieren und viel Zeit zu vergeuden ... lies endlich mal meinen 
Beitrag!

"...4 x mit goto zum gleichen return springen ..UND VORHER immer die 
gleiche
Funktion error(blah_blah) aufrufen ..."

von Bernd K. (prof7bit)


Lesenswert?

Walter K. schrieb:
> "...4 x mit goto zum gleichen return springen ..UND VORHER immer die
> gleiche
> Funktion error(blah_blah) aufrufen ..."

Ja, und das ist falsch, denn er ruft nicht die funktion error(blah blah) 
auf und springt DANACH per goto zum return.

Ich weiß nicht was für Dich das größere Problem ist, den Code überhaupt 
erstmal zu lesen über den Du Dich so grundlos aufbläst oder in Worte zu 
fassen was Du eigentlich mitteilen willst. Und wir wissen immer noch 
nicht was Du eigentlich sagen willst oder was an dem Code den Du nicht 
verstanden hast Deiner Meinung nach falsch sein soll.

von Rolf M. (rmagnus)


Lesenswert?

Walter K. schrieb:
> 4 x mit goto zum gleichen return springen ..und vorher immer die gleiche
> Funktion error(blah_blah) aufrufen - um damit die Sinnhaftigkeit von
> goto in C zu demonstrieren ..... ohne Worte!!!

Mir erschließt sich nicht, wo du da ein Problem siehst. Muss man deiner 
Meinung nach mit jedem goto zu einem anderen return springen? Und darf 
man vor diesen returns keine Funktion aufrufen? Oder irgendwie wieder 
verzweigen, damit es immer eine andere Funktion ist? Deshalb ist das 
goto ja da, weil es immer die selbe Kombination aus dem Funktionsaufruf 
und dem return ist. Was ist dein Alternativ-Vorschlag? Viermal den 
gleichen Funktionsaufruf mit jeweils darauf folgendem return 
hinschreiben?

Walter K. schrieb:
> Mein Gott ... anstatt Dich hier aufzublasen, von Kohärenzen zu
> schwadronieren und viel Zeit zu vergeuden ... lies endlich mal meinen
> Beitrag!

Das haben wir hier alle.

> "...4 x mit goto zum gleichen return springen ..UND VORHER immer die
> gleiche Funktion error(blah_blah) aufrufen ..."

Ja, die Funktion wird aber nicht VOR dem goto aufgerufen, und es wird 
nicht zum return gesprungen. Ich vermute, du hast dich nur ungeschickt 
ausgedrückt. Und dein "..." besteht dann nur noch aus "ohne Worte" mit 
drei Ausrufezeichen. Das ist keine Erklärung.

Karl schrieb:
> Ich hab auch 2 unreachable code-Warnungen in meinem aktuellen Projekt.
> Ich inkrementiere einen uint8 und wenn der Buffermax erreicht
> (Bufferlänge) setze ich ihn zurück. Nun ist Buffermax = 256, wird also
> nie erreicht bzw. automatisch wieder Null. Trotzdem lasse ich die
> Abfrage drin, weil ich ja mal die Bufferlänge verringern könnte und dann
> ist die wichtig. Bekomme also immer Compilerwarnungen, hab aber auch
> keine andere Lösung dafür. (Nein, uint16 verwenden ist keine Lösung ;-))

Mit einem #if prüfen, ob deine maximale Bufferlänge UINT8_MAX 
entspricht, wäre zu einfach? Bei der Gelegenheit könntest du auch gleich 
noch ein zweites #if einbauen, das zu einem Fehler führt, wenn du 
versehentlich mal einen Wert einträgst, der größer ist.

: Bearbeitet durch User
von Programmiersprachentheaterintendant (Gast)


Lesenswert?

>>
1
>> []{
2
>> if (a) return;
3
>> if (b) return;
4
>> if (c) return;
5
>> if (d) return;
6
>> doSomething();
7
>> }();
8
>>
>
> Gerade noch eingefallen ...
>
>
1
>     [](auto... cc) {
2
>          (f(cc) && ... && doSth());
3
>     }(c, '@', '$');
4
>
>
> In der Kürze liegt die Würze.

Ist das schon Perl oder kann das weg?

---

Mir gefallen die Ansätze mit "@#A0" und Schlaufe besser: diese bringen 
das Konzept Programmieren her und ist Zukunftgerichtet. Sollten andere 
Prüffälle nötig sein bleibt die Programmlogik unangetastet, das kann 
sogar in eine parametrierte Funktion ausfaktoriert werden; 
bewährter/getesteter Code (durchaus auch in Binärform) in einer lib 
zur Wiederverwendung vorgehalten werden.

Alles andere ist... Sackgasse ?

von Wilhelm M. (wimalopaan)


Lesenswert?

Programmiersprachentheaterintendant schrieb:
>>>
1
>>> []{
2
>>> if (a) return;
3
>>> if (b) return;
4
>>> if (c) return;
5
>>> if (d) return;
6
>>> doSomething();
7
>>> }();
8
>>>
>>
>> Gerade noch eingefallen ...
>>
>>
1
>>     [](auto... cc) {
2
>>          (f(cc) && ... && doSth());
3
>>     }(c, '@', '$');
4
>>
>>
>> In der Kürze liegt die Würze.
>
> Ist das schon Perl oder kann das weg?

Wenns Perl wäre, könnte es in der Tat weg ...

Die Zeichen wie '@' oder '$' stammen vom TO.

>
> Mir gefallen die Ansätze mit "@#A0" und Schlaufe besser: diese bringen
> das Konzept Programmieren her und ist Zukunftgerichtet.

Was meinst Du denn da mit? Und was ist Schlaufe ?

> Sollten andere
> Prüffälle nötig sein bleibt die Programmlogik unangetastet, das kann
> sogar in eine parametrierte Funktion ausfaktoriert werden;

Was ist denn bei Dir ein parametrierte Funktion? Meinst Du parametrische 
Polymorphie?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
>> Mir gefallen die Ansätze mit "@#A0" und Schlaufe besser: diese bringen
>> das Konzept Programmieren her und ist Zukunftgerichtet.
>
> Was meinst Du denn da mit? Und was ist Schlaufe ?

Das ist Schweizerdeutsch und heißt auf Deutschdeutsch "Schleife" ;-)

Der  "Programmiersprachentheaterintendant" bezieht sich vermutlich auf
den folgenden Vorschlag, wo die zu prüfenden Zeichen in einer expliziten
Schleife abgearbeitet werden:

A. schrieb:
> char * zeichen = "@#A0";
>
> for(uint8_t i = 0; i < strlen(zeichen); i++){
>   if(foo(prm, zeichen[i]) != EXIT_FAILURE){
>     return EXIT_SUCCESS;
>   }
> }
>
> return EXIT_FAILURE;

Für mich persönlich ist es prinzipiell guter Stil, explizite Schleifen
durch implizite zu ersetzen, wie du es in deinem Beispiel getan hast.
Folds sind generell eine gute Möglichkeit zur Notation akkumulierender
Berechnungen/Auswertungen, und werden deswegen von den meisten neueren
Programmiersprachen unterstützt, im Fall von C++17 sogar durch eine
spezielle Syntax.

Was mir bei den C++17-Folds aber nicht so sehr gefällt ist die Tatsache,
dass sie die zu faltende Sequenz in Form eines Parameter-Packs erwarten,
weswegen du in deinem Beispiel den Fold in einen Lambda-Ausdruck packen
musstest, der einzig und allein dazu dient, die Sequenz der zu prüfenden
Zeichen in das passende Format zu konvertieren.

In funktionalen Sprachen, aus denen die Folds übernommen wurden, sieht
das i.Allg. sehr viel eleganter aus. So würde man bspw. den C++-Ausdruck

1
  [](auto... cc) { (f(cc) && ...); } ('@', '#', 'A', '0')

in Haskell einfach als

1
  all f "@#A0"

schreiben. Die Bibliotheksfunktion all prüft, ob die Funktion f für
jedes Element des zweiten Arguments True ergibt.

Aber wir schweifen ab. Der TE programmiert in C, und da gibt es Folds
weder im Sprachumfang noch in der Standardbibliothek. Unter dieser
Einschränkung ist obige Schleifenlösung IMHO schon in Ordnung.

von mh (Gast)


Lesenswert?

Yalu X. schrieb:
> In funktionalen Sprachen, aus denen die Folds übernommen wurden, sieht
> das i.Allg. sehr viel eleganter aus. So würde man bspw. den C++-Ausdruck
>
>   [](auto... cc) { (f(cc) && ...); } ('@', '#', 'A', '0')
>
> in Haskell einfach als
>
>   all f "@#A0"
>
> schreiben. Die Bibliotheksfunktion all prüft, ob die Funktion f für
> jedes Element des zweiten Arguments True ergibt.

Es gibt seit C++11 all_of 
(http://en.cppreference.com/w/cpp/algorithm/all_any_none_of). Nicht ganz 
das gleiche, da es auf Iteratoren basiert, aber "bald" gibt es all_of 
auch für ranges dank ranges-TS 
(http://en.cppreference.com/w/cpp/experimental/ranges/algorithm/all_any_none_of).

von Yalu X. (yalu) (Moderator)


Lesenswert?

mh schrieb:
> Es gibt seit C++11 all_of
> (http://en.cppreference.com/w/cpp/algorithm/all_any_none_of). Nicht ganz
> das gleiche, da es auf Iteratoren basiert,

Ja, diese Funktionen mit Iteratoren als Argumente sind aber gruselig,
wenn man in 99% der Fälle den gesamten Container bearbeiten möchte und
statt des Variablennamens immer dieses lästige begin()-/end()-Gedöns
hinschreiben muss. Die Idee ist gut, die Umsetzung schlecht.

> aber "bald" gibt es all_of auch für ranges dank ranges-TS
> 
(http://en.cppreference.com/w/cpp/experimental/ranges/algorithm/all_any_none_of).

Danke für die Info. Darauf warte ich schon mindestens 20 Jahre.

Ich mochte C++ ja noch nie so besonders und nutze es – dort, wo ich es
nutze – eher aus Mangel vernünftiger Alternativen denn aus Überzeugung.
Wenn ich aber die Fortschritte seit C++11 und die angekündigten
zukünftigen Erweiterungen verfolge, sehe ich mich auf dem besten Weg,
irgendwann doch noch C++-Fan zu werden :)

Ich frage mich nur, warum es fast 3 Jahrzehnte dauern musste, bis dieser
plötzliche Schub zum Positiven hin eingesetzt hat.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> mh schrieb:
>> Es gibt seit C++11 all_of
>> (http://en.cppreference.com/w/cpp/algorithm/all_any_none_of). Nicht ganz
>> das gleiche, da es auf Iteratoren basiert,
>
> Ja, diese Funktionen mit Iteratoren als Argumente sind aber gruselig,
> wenn man in 99% der Fälle den gesamten Container bearbeiten möchte und
> statt des Variablennamens immer dieses lästige begin()-/end()-Gedöns
> hinschreiben muss.

Normalerweise schreibt man sich dafür einen Adapter, in diesem Fall ein 
3-zeiliges Funktionstemplate ...

> Die Idee ist gut, die Umsetzung schlecht.

Naja, die Idee von Stepanov, die Iteratoren als das Bindeglied zwischen 
den Containern und den Algorithmen einzusetzen, war schon genial. 
Natürlich kann man es jetzt mit concepts (ranges) besser machen ...

>> aber "bald" gibt es all_of auch für ranges dank ranges-TS

Schau bei Eric nach, der Entwurf ist dort schon recht lange zu finden.

> Danke für die Info. Darauf warte ich schon mindestens 20 Jahre.

Da hättest Du aber auch schon std::find_if_not (evtl. mit dem o.g. 
Adapter) verwenden können.

> Ich mochte C++ ja noch nie so besonders und nutze es – dort, wo ich es
> nutze – eher aus Mangel vernünftiger Alternativen denn aus Überzeugung.
> Wenn ich aber die Fortschritte seit C++11 und die angekündigten
> zukünftigen Erweiterungen verfolge, sehe ich mich auf dem besten Weg,
> irgendwann doch noch C++-Fan zu werden :)

Und für mich ist es fast unbegreiflich, wie man so hartnäckig an purem C 
festhalten kann: ich bin davon überzeugt, dass man 98% der Module einer 
µC-SW direkt mit einem C++-Compiler übersetzen kann, und dabei kann man 
sich ganz behutsam die Rosinen aus der Sprache heraus picken (aber ich 
wiederhole mich ...)

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.