error: jump to case label [gelöst]

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
dakuan
Beiträge: 97
Registriert: 28.04.2011 22:09:39

error: jump to case label [gelöst]

Beitrag von dakuan » 02.09.2021 19:19:52

Ich bekomme folgende Fehlermeldung:

Code: Alles auswählen

ThumbBox.cpp: In member function ‘virtual int ThumbBox::handle(int)’:
ThumbBox.cpp:96:14: error: jump to case label [-fpermissive]
         case FL_PUSH:
              ^~~~~~~
ThumbBox.cpp:84:20: note:   crosses initialization of ‘char* p’
             char * p = evt_txt;
                    ^
makefile:56: recipe for target 'ThumbBox.o' failed
Ich kann diese Meldung zwar irgendwie "wegspielen" aber ich verstehe die eigentliche Ursache nicht.

Code: Alles auswählen

//-----------------------------------------------------------------------------
//      Event handler
int ThumbBox::handle( int event ) {
    const Fl_Menu_Item * m;
    int mouse_button;

    switch( event ) {
        case FL_ENTER:
            return 1;

        case FL_DND_ENTER:          // return(1) for these events to 'accept' dnd
            Fl::focus( this );
        case FL_DND_LEAVE:
        case FL_DND_DRAG:
        case FL_DND_RELEASE:
            evt = event;
            return 1;

        case FL_PASTE:
            evt = event;
            evt_len = Fl::event_length() + 1;
            delete [] evt_txt;
            evt_txt = new char[evt_len];
            strcpy( evt_txt, Fl::event_text() );
            char * p = evt_txt;    // <<<< diese Zeile wird angemeckert <<<<
            while( *p ) {
                if( *p == '\r' || *p == '\n' ) {
                    *p = '\0';
                    break;
                }
                p++;
            }
            fl_decode_uri( evt_txt );
            Fl::add_timeout( 0.0, ThumbBox::callback_deferred, (void*)this );
            return 1;

        case FL_PUSH:
            mouse_button = Fl::event_button();
            if( mouse_button == FL_RIGHT_MOUSE  &&  popup_menu != 0 ) {
                rm_x_pos = Fl::event_x();
                rm_y_pos = Fl::event_y();
                m = popup_menu->popup( Fl::event_x(),Fl::event_y(),0,last_picked,0 );
                if( m  &&  m->callback() != 0 ) {
                    last_picked = m;
                    m->do_callback( this, m->user_data() );
                }
                return 1;
            }
            break;
    }
    return( ImageBox_::handle( event ) );
}
Der Code hat bisher immer anstandslos funktioniert, bis ich den Zweig mit FL_PUSH eingebaut habe. Damit soll eigenlich nur ein lokales Menü eingeblendet werden. Der Zeiger p wird doch außerhalb von FL_PASTE nicht mal angesehen.

Hat jemand eine Ahnung, warum das ein Fehler ist?

ps. In der while() Schleife sollen nur Zeilenwechsel etfernt werden, die einige Programme bei Drag_and_Drop an den String drannhängen.
Zuletzt geändert von dakuan am 03.09.2021 13:38:56, insgesamt 1-mal geändert.

eggy
Beiträge: 3331
Registriert: 10.05.2008 11:23:50

Re: error: jump to case label

Beitrag von eggy » 02.09.2021 20:18:06

Versuch mal ob es so geht:

Code: Alles auswählen

char *p = nullptr;
switch ...
case ...
   p= irgendwas
wenn's geht, lag's daran, dass man keine neuen Variablen innerhalb von case anlegen soll.

und schau dir mal https://en.cppreference.com/w/cpp/langu ... allthrough an, es hilft beim Lesen sehr, wenn man nicht überlegen muss, ob nen break vergessen wurde.

dakuan
Beiträge: 97
Registriert: 28.04.2011 22:09:39

Re: error: jump to case label

Beitrag von dakuan » 02.09.2021 20:52:30

wenn's geht, lag's daran, dass man keine neuen Variablen innerhalb von case anlegen soll.
Ja, so hatte ich das bisher immer "weggespielt", Was ich aber unschön finde. Andererseits wird von einigen Hardlinern ja immer gefordert, Variablen dort zu deklarieren, wo sie benötigt werden.

Ich wollte jetzt den FL_PUSH Zweig nach oben verschieben, da das ja vermutlich nur nachfolgende Zweige betrifft. Aber dann hatte ich noch eine andere Idee. Ich hatte im Quelltext von FLTK immer wieder mal vermeintlich überflüssige Klammern entdeckt. Das habe ich jetzt einfach mal ausprobiert:

Code: Alles auswählen

        case FL_PASTE:              // handle actual drop (paste) operation
            evt = event;
            evt_len = Fl::event_length() + 1;
            delete [] evt_txt;      // todo: nur wenn nötig - Länge vergleichen
            evt_txt = new char[evt_len];
            strcpy( evt_txt, Fl::event_text() );
            {   char * p = evt_txt;
                while( *p ) {
                    if( *p == '\r' || *p == '\n' ) {
                        *p = '\0';
                        break;
                    }
                    p++;
                }
            }
            fl_decode_uri( evt_txt );
            Fl::add_timeout( 0.0, ThumbBox::callback_deferred, (void*)this );
            return 1;
Wenn ich das jetzt richtig gelesen habe, habe ich damit einen anonymen Namespace eingefügt. Das scheint da Problem auch zu lösen. Aber wirklich verstehen tue ich das immer noch nicht.

Ist das jetzt eine saubere Lösung oder auch nur ein Würgaraound?

JTH
Moderator
Beiträge: 3023
Registriert: 13.08.2008 17:01:41
Wohnort: Berlin

Re: error: jump to case label

Beitrag von JTH » 02.09.2021 21:12:43

switch-case sind in C und C++ etwas eigenartige Konstrukte bzw. der Compiler macht evtl. unerwartete Dinge daraus. Deshalb haben sie, wie du beobachtet hast, ein paar Einschränkungen. (Wer sucht, findet bestimmt auch eine bessere und richtige Erklärung dafür. Fehlt mir grad die Zeit für ;) )

dakuan hat geschrieben: ↑ zum Beitrag ↑
02.09.2021 20:52:30
Wenn ich das jetzt richtig gelesen habe, habe ich damit einen anonymen Namespace eingefügt.
[…]
Ist das jetzt eine saubere Lösung oder auch nur ein Würgaraound?
Nein, das ist kein anonymer Namespace. Dazu fehlt das namespace-Schlüsselwort und den benutzt man auch an anderen Stellen.

Was du dort eingefügt hast, ist ein neuer Block/Scope. Und das ist – wenn ich mich grad nicht völlig falsch erinnere – tatsächlich die richtige und einzige Möglichkeit, innerhalb eines case eine neue Variable zu deklarieren. Die neue Variable ist damit – wie das bei Scopes eben so ist – nur innerhalb der { } verfügbar. Anders gehts nicht.
Manchmal bekannt als Just (another) Terminal Hacker.

dakuan
Beiträge: 97
Registriert: 28.04.2011 22:09:39

Re: error: jump to case label

Beitrag von dakuan » 02.09.2021 21:28:25

Ok, den Begriff Block/Scope kannte ich bisher noch nicht. Aber wenn das ein korrekter Weg ist, kann ich damit leben und werde das in mein Kochbuch aufnehmen.

Benutzeravatar
bluestar
Beiträge: 2346
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: error: jump to case label

Beitrag von bluestar » 03.09.2021 08:12:30

JTH hat geschrieben: ↑ zum Beitrag ↑
02.09.2021 21:12:43
switch-case sind in C und C++ etwas eigenartige Konstrukte bzw. der Compiler macht evtl. unerwartete Dinge daraus. Deshalb haben sie, wie du beobachtet hast, ein paar Einschränkungen. (Wer sucht, findet bestimmt auch eine bessere und richtige Erklärung dafür. Fehlt mir grad die Zeit für ;) )
Betrachte mal den nachfolgenden Code:
* Wenn event==1 dann wird char *p = ev_txt gesetzt und danach erfolg KEIN break also läuft die der Code mit do_something(p) weiter. => Alles bestens
* Wenn event==2 dann müsste do_something(p) ausgeführt werden, jedoch ist p in dem Falle unbekannt/nicht definiert.

Code: Alles auswählen

switch (event) {
    case (1): 
                  char * p = evt_txt; 
     case (2):
                  do_something(p);
                  break;
}
Mit einem eigenen Scope:
Betrachte mal den nachfolgenden Code:
* hier ist p nur innerhalb des Scopes gültig und somit entsteht bei event==2 kein Problem mit p, weil "p" ganz klar undefined ist.

Code: Alles auswählen

switch (event) {
    case (1): 
                 {
                       char * p = evt_txt; 
                       do_this_with(p);
                 }
                 
                 // hier ist p schon wieder unbekannt, weil außerhalb des scopes
                 
     case (2):
                  do_something_else(p);  // Fehler: p is undefined
                  break;
}
genaueres gibt's auch hier: https://en.cppreference.com/w/cpp/language/switch

Benutzeravatar
Meillo
Moderator
Beiträge: 8813
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: error: jump to case label

Beitrag von Meillo » 03.09.2021 08:59:45

Die Hinweise zu Scopes und Variablendefinitionen sind natuerlich richtig. Und das ist IMO auch der richtige Weg das zu loesen, also entweder die Variable zu Beginn schon zu definieren oder eben einen eigenen Scope in den Case einzufuegen.

Ich frage mich jedoch, was genau die Fehlermeldung aussagt und warum sie kommt.

In C++ muessen Variablendefinitionen ja nicht mehr zu Beginn des Blocks erfolgen, sondern koennen irgendwo im Code auftauchen. Insofern sollte es egal sein wo `p' definiert wird, solange jede Verwendung danach ist. Das ist AFAICS hier gegeben, denn `p' kommt nur in dem einen Case vor. Auch Fall-Throughs und sonstige seltsame Wege, die zu einem Problem fuehren koennten, sehe ich nicht.

Die Fehlermeldung sagt (uebersetzt, so wie ich das verstehe), dass der Sprung zum Case-Label ueber die Initialisierung von `p' hinweggeht. Von einem ``Sprung'' wird hier geredet, weil Switch-Case mittels bedingten Gotos umgesetzt wird.

Ich wuerde verstehen, wenn hier ein Hinweis kommt, dass hier ein potenzielles Problem vorliegen koennte, aber ich verstehe derzeit nicht, warum der Compiler abbricht und unfaehig scheint, das compilieren zu koennen. Ich sehe also nicht warum der Code nicht kompiliert werden *kann*.


Wenn ich mir das Problem mal minimal vorstelle, dann waere das doch:

Code: Alles auswählen

goto a;
char *p = "foo";
a:
... und dann meckert der Compiler weil er die Variablendefinition gerne entweder vor dem Goto haette oder in einem separaten Scope. Habe ich das richtig verstanden?

Oder liegt das Problem nur daran was JTH schreibt, dass Switch-Case vom Compiler ``seltsam'' umgesetzt wird?

Kann mich jemand erhellen?
Use ed once in a while!

Benutzeravatar
bluestar
Beiträge: 2346
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: error: jump to case label

Beitrag von bluestar » 03.09.2021 10:01:57

Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 08:59:45
Ich frage mich jedoch, was genau die Fehlermeldung aussagt und warum sie kommt.
Da bist du mit deinem Goto-Verständnis auf der richtigen Spur.
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 08:59:45
In C++ muessen Variablendefinitionen ja nicht mehr zu Beginn des Blocks erfolgen, sondern koennen irgendwo im Code auftauchen. Insofern sollte es egal sein wo `p' definiert wird, solange jede Verwendung danach ist. Das ist AFAICS hier gegeben, denn `p' kommt nur in dem einen Case vor. Auch Fall-Throughs und sonstige seltsame Wege, die zu einem Problem fuehren koennten, sehe ich nicht.
Auch in C++ steckt an manchen Ecken noch plain-old-c drinnen, bei switch/case/goto ist das genau der Fall. Du hast recht, dass der Compiler, wenn er den Code nicht nur compilieren sondern auch verstehen würde, erkennen könnte das durch die "return"-Zeile der Fehler gar nicht auftreten kann. Der Compiler versteht den Code jedoch nicht, also moniert er das es ein Sprungziel in den aktuellen Block nach der Variablendefinition gibt.
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 08:59:45
Die Fehlermeldung sagt (uebersetzt, so wie ich das verstehe), dass der Sprung zum Case-Label ueber die Initialisierung von `p' hinweggeht. Von einem ``Sprung'' wird hier geredet, weil Switch-Case mittels bedingten Gotos umgesetzt wird.
Richtig erkannt.
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 08:59:45
aber ich verstehe derzeit nicht, warum der Compiler abbricht und unfaehig scheint, das compilieren zu koennen.
Der Code ist nunmal fehlerhaft.
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 08:59:45
Ich sehe also nicht warum der Code nicht kompiliert werden *kann*.
Du verstehst den Code und bist deinem Compiler dadurch überlegen, ich hatte oben ja bereits erwähnt das der Compiler eben nur Anweisung nach Anweisung übersetzt und eben nicht nachdenkt.
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 08:59:45
Wenn ich mir das Problem mal minimal vorstelle, dann waere das doch:

Code: Alles auswählen

goto a;
char *p = "foo";
a:
... und dann meckert der Compiler weil er die Variablendefinition gerne entweder vor dem Goto haette oder in einem separaten Scope. Habe ich das richtig verstanden?
Letztlich ist die Implementation von Switch/Case auch noch compilerabhängig, es gibt durchaus Compiler die switch/case als if/else fi implementieren, wenn du an solch einen Compiler gerätst, lässt sich der Code problemlos durchcompilieren.

eggy
Beiträge: 3331
Registriert: 10.05.2008 11:23:50

Re: error: jump to case label

Beitrag von eggy » 03.09.2021 10:28:01

@bluestar: war es nicht so, dass C und C++ das unterschiedlich umsetzen? Müsst man mal in die Grammatik schauen, aber wenn man nen Stück Minimalcode mit gcc/g++/clang/clang++ übersetzt gibt's zumindest unterschiedliche Hinweise.

Ich hab dazu vor Jahren dazu mal was gehört, krieg es aber aktuell nicht zusammen, falls es mir einfällt liefer ich die Quelle nach.

Benutzeravatar
bluestar
Beiträge: 2346
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: error: jump to case label

Beitrag von bluestar » 03.09.2021 10:43:25

eggy hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 10:28:01
@bluestar: war es nicht so, dass C und C++ das unterschiedlich umsetzen?
C und C++ sind erstmal ja nur Sprachdialekte, die Umsetzung obliegt letztlich dem Compiler...
eggy hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 10:28:01
Müsst man mal in die Grammatik schauen, aber wenn man nen Stück Minimalcode mit gcc/g++/clang/clang++ übersetzt gibt's zumindest unterschiedliche Hinweise.
Oder direkt mal in die Sourcen der Compiler schauen, wie es implementiert ist... Dann hast du die harten (kryptischen) und unverständlichen Fakten vor dir.
eggy hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 10:28:01
Ich hab dazu vor Jahren dazu mal was gehört, krieg es aber aktuell nicht zusammen, falls es mir einfällt liefer ich die Quelle nach.
Lach wenn du die Links gefunden hast, dann freue ich mich, weil ich meine Links dazu auch gerade nicht finde.

eggy
Beiträge: 3331
Registriert: 10.05.2008 11:23:50

Re: error: jump to case label

Beitrag von eggy » 03.09.2021 13:07:14

Naja, nen Compiler ist ja auch "nur" ne Sichtweise, im Sprachstandard sollte aber drinstehen was geht.
Suchmaschine spuckt folgendes aus
https://cs.wmich.edu/~gupta/teaching/cs ... 20form.htm
Zumindest in der Form gibt die Grammatik es nicht her, dass man ne "declaration" ohne umgebende "{}" nach nem "case" hinbekommt.
Allerdings "This grammar was adapted from Section A13 of The C programming language, 2nd edition, by Brian W. Kernighan and Dennis M. Ritchie,Prentice Hall, 1988.", vermutlich gibt's noch was besseres, falls jemand drüber stolpert kann er es ja mal verlinken.

Benutzeravatar
bluestar
Beiträge: 2346
Registriert: 26.10.2004 11:16:34
Wohnort: Rhein-Main-Gebiet

Re: error: jump to case label

Beitrag von bluestar » 03.09.2021 13:30:19

Für C++20 gibt's das auch: https://alx71hub.github.io/hcb/ deutlich komplexer aber durchschaubar ;)

dakuan
Beiträge: 97
Registriert: 28.04.2011 22:09:39

Re: error: jump to case label

Beitrag von dakuan » 03.09.2021 13:38:18

In den meisten Fällen möchte ich die Variablendefinitionen auch am Beginn einer Funktion haben. Aber das ist nicht immer zweckmäßig, z.B. wenn man mit bedingter Compilierung arbeitet.

Ich habe mir jetzt die Beiträge mehrfach durchgelesen und habe jetzt den Verdacht, das der Compiler das ganz einfach sieht. Der sieht nur Sprünge und Klammern. Ob da noch ein break oder return steht, interessiert nicht. Das habe ich ausprobiert.

Wahrscheinlich geht er davon aus, das eine neue Variable von ihrer Position im Code an gültig ist bis zum ende das Blocks (also hier Switch-Ende). Der Compiler sieht dann nur, das über die Definition hinweggesprungen wird. Da muss man aber erstmal drauf kommen.

Benutzeravatar
Meillo
Moderator
Beiträge: 8813
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: error: jump to case label

Beitrag von Meillo » 03.09.2021 14:47:44

dakuan hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 13:38:18
Wahrscheinlich geht er davon aus, das eine neue Variable von ihrer Position im Code an gültig ist bis zum ende das Blocks (also hier Switch-Ende).
Dem ist so.
Der Compiler sieht dann nur, das über die Definition hinweggesprungen wird. Da muss man aber erstmal drauf kommen.
Aber ich verstehe immer noch nicht (auch bluestars Post hat daran nichts geaendert), warum er sich weigert das zu akzeptieren.

De facto ist die Variable an allen Stellen bekannt wo sie verwendet wird. Dass der Programmflow etwas ungewoehnlich ist, mag ja sein, aber warum informiert er nicht nur darueber, sondern bricht ab? Was genau ist der Grund, wegen dem er unfaehig ist, einfach Code zu erzeugen?

Wenn ihr sagt: ``Ist halt so'' oder ``er ist halt defensiv'', dann will ich das nicht akzeptieren. So schlecht sind die Compiler heutzutage nicht mehr. ... Die ganze Zeit habe ich das Gefuehl, dass ich irgendwas offensichtliches uebersehe ... :roll:
Use ed once in a while!

dakuan
Beiträge: 97
Registriert: 28.04.2011 22:09:39

Re: error: jump to case label [gelöst]

Beitrag von dakuan » 03.09.2021 15:22:25

Ich denke, der Compiler macht sich das sehr einfach, indem er erstmal alles, was innerhalb des switch() Blocks als ein Scope sieht.

Meillo schrieb:
Dass der Programmflow etwas ungewoehnlich ist, mag ja sein, aber warum informiert er nicht nur darueber, sondern bricht ab?
Den ungewöhnlichen Programmflow kann ich erklären. Das ist ein Event-Handler einer abgeleiteten Klasse. Der Rückgabewert 1 bedeutet "das Event wurde bearbeitet", da kann ich also direkt raus.

Es kann aber sein, dass die Klasse, von der ich abgeleitet habe, die Events haben will, die hier nicht beachtet wurden. Da müsste ich dann noch eine Ergebnisvariable mitführen und am Ende nochmal abfragen um dann zu entscheiden, ob der ursprüngliche Event-Handler noch informiert werden muss.

Benutzeravatar
Meillo
Moderator
Beiträge: 8813
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: error: jump to case label [gelöst]

Beitrag von Meillo » 03.09.2021 15:51:54

dakuan hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 15:22:25
Ich denke, der Compiler macht sich das sehr einfach, indem er erstmal alles, was innerhalb des switch() Blocks als ein Scope sieht.
Das ist nunmal das Design von Switch in C/C++ ... Das ist halt sehr assemblerartig und nicht so modern wie das if in der gleichen Sprache. Und ja, das Design ist bestimmt auch so, weil das den Compiler einfacher gemacht hat ... also Anfang der 70er Jahre wo C entstanden ist ... und die Designidee ist sicher noch aelter, man hat sie an der Stelle einfach uebernommen, waehrend man bei den Schleifen z.B. modernere Designs gewaehlt hat, als damals ueblich.
Meillo schrieb:
Dass der Programmflow etwas ungewoehnlich ist, mag ja sein, aber warum informiert er nicht nur darueber, sondern bricht ab?
Den ungewöhnlichen Programmflow kann ich erklären. Das ist ein Event-Handler einer abgeleiteten Klasse. Der Rückgabewert 1 bedeutet "das Event wurde bearbeitet", da kann ich also direkt raus.

Es kann aber sein, dass die Klasse, von der ich abgeleitet habe, die Events haben will, die hier nicht beachtet wurden. Da müsste ich dann noch eine Ergebnisvariable mitführen und am Ende nochmal abfragen um dann zu entscheiden, ob der ursprüngliche Event-Handler noch informiert werden muss.
Ich habe eigentlich die Umsetzung eines Switch im vom Compiler erzeugten Code gemeint, was halt eine Goto-Springerei ist, ohne Scope und so.

An den Programmflow, wie du ihn verstanden hast, habe ich gar nicht gedacht ... und damit auch nicht an die Konsequenzen seiner Asynchronitaet. Jedoch sollte das nicht relevant sein, da die Definition und alle Verwendungen von `p' nur an einer Stelle sind und dazwischen nichts sonstiges passiert.



Es ist ja schon so, dass dieses ganze Switch-Zeug in C/C++ so seine Tuecken hat. Beispielsweise diese Sache:

Code: Alles auswählen

:-Q cat a.c
#include <stdio.h>

int
main(void)
{
        char c = 'x';

        switch (c) {
        case 'x':
                int i = 1;
                printf("i:%d\n", i);
        }
        return 0;
}


:-Q make a 
cc     a.c   -o a
a.c: In function ‘main’:
a.c:10:3: error: a label can only be part of a statement and a declaration is not a statement
make: *** [a] Error 1

:-Q 
... das kann ich aber nachvollziehen und verstehe die Gruende, waehrend das bei der Problem in diesem Thread nicht so ist.

Vielleicht sollten wir mal ein Minimalbeispiel erstellen, um der Sache naeher zu kommen ...
Use ed once in a while!

eggy
Beiträge: 3331
Registriert: 10.05.2008 11:23:50

Re: error: jump to case label

Beitrag von eggy » 03.09.2021 15:59:28

Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 14:47:44
Die ganze Zeit habe ich das Gefuehl, dass ich irgendwas offensichtliches uebersehe ... :roll:
Die Grammatik. Nimm die oben verlinkte C Grammatik, die liest sich leichter.
Falls jemanden "BNF" nichts sagt,
https://de.wikipedia.org/wiki/Backus-Naur-Form
https://de.wikipedia.org/wiki/Erweitert ... -Naur-Form
kurz: das ist eine Art, mit der man beschreiben kann, wie eine Sprache aufgebaut ist. Es wird so das Regelwerk festgelegt, mit dem die Compiler arbeiten. Und es gilt: was da nicht drinsteht, "gibt's nicht" (aka undefined behavior, dann macht das Ding eben was es will - bzw. was Compilerbauer oder Mondphase sagen). Oftmals gibts dazu auch eine Darstellung als (Teil-)Graph.

Wenn man in die Zeile

Code: Alles auswählen

<labeled-statement> ::= <identifier> : <statement>
                      | case <constant-expression> : <statement>
                      | default : <statement>
schaut, sieht man: ein "label-statement" besteht entweder aus "identifier : statement" oder "case contant-expression : statement" oder "default : statement".
Also schaut man sich als nächstes "constant-expression" und "statement" an.
Und wenn man die alle solange verfolgt hat, bis man merkt nur noch im Kreis zu laufen, dann hat man rausgefunden: "declaration" kann in dem Teil des Graphen nur noch über "compound-statement" erreicht werden, und da sind halt die "{" "}" drum rum.

eggy
Beiträge: 3331
Registriert: 10.05.2008 11:23:50

Re: error: jump to case label [gelöst]

Beitrag von eggy » 03.09.2021 16:14:42

@Meillo: falls Dich die Thematik interessiert, schau dass Du ne alte Ausgabe vom "Drachenbuch" (Compilerbau von Aho - ja, der awk-Aho) bekommst. Die ganz alten Bände hatten ihre Beispiele noch in C Code, die neueren Versionen dann mit Java. Neuer ist wohl leichter/verständlicher(?) zu lesen, das alte für C Freunde vermutlich ergiebiger.


Makefile:

Code: Alles auswählen

OPTIONS= -O0 -S

all:
        @echo  "--------------------------------"
        @echo  " "
        - clang $(OPTIONS) clang_test_c.c  
        @echo  " "
        @echo  "--------------------------------"
        @echo  " "
        - clang++ $(OPTIONS)  clang_test_cpp.cpp  
        @echo  " "
        @echo  "--------------------------------"
        @echo  " "
        - gcc  $(OPTIONS) gcc_test_c.c  
        @echo  " "
        @echo  "--------------------------------"
        @echo  " "
        - g++  $(OPTIONS) gcc_test_cpp.cpp
        @echo  " "
        @echo  "--------------------------------"
        @echo  " "
test.irgendwas

Code: Alles auswählen

int main() {
                int value = 1;
                switch (value) {  
                                case 1: 
                                                int i = 23;
                                                break;

                                case 2: 
                                                return 2;
                }
}

Code: Alles auswählen

ln test.irgendwas clang_test_c.c
ln test.irgendwas clang_test_cpp.cpp
ln test.irgendwas gcc_test_c.c
ln test.irgendwas gcc_test_cpp.cpp
Oder hat jemand ne bessere Idee?

Benutzeravatar
Meillo
Moderator
Beiträge: 8813
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: error: jump to case label

Beitrag von Meillo » 03.09.2021 17:17:18

eggy hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 15:59:28
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 14:47:44
Die ganze Zeit habe ich das Gefuehl, dass ich irgendwas offensichtliches uebersehe ... :roll:
Die Grammatik. [...] dann hat man rausgefunden: "declaration" kann in dem Teil des Graphen nur noch über "compound-statement" erreicht werden, und da sind halt die "{" "}" drum rum.
Das ist schon klar. Warum nach einem Label keine Deklaration kommen kann, das wusste ich schon und habe damals dazu die Grammatik auch gelesen. (Das Dragon Book habe ich auch in meinem Buecherregel. Irgendwann habe ich auch mal den Anfang gelesen, aber weil ich nur gelesen und nicht parallel die Dinge auch ausprobiert habe, hat es dann irgendwann keinen Sinn mehr gemacht weiterzulesen, weil's ohne die Praxis zu abstrakt geworden ist.)

Wie dem auch sei. Mit meinem ``ich glaube, ich uebersehe etwas'' meine ich den Code von dakuan. Ich hatte nicht verstanden, welchen Grund der Compiler hat, diesen Fehler zu werfen.


Nach eigenem Testen und Recherchieren kann ich die Antwort nun aber geben. Der erleuchtende Satz war dieser (irgendwo aus dem GCC Bugtracker):``-fpermissive is a band-aid to work around broken code''. Der g++ verhindert hier, dass Code, der als unschoen und potenziell problematisch angesehen wird, durchgeht. Stattdessen sagt er einfach: Sowas macht man nicht! Aetsch!

Es gibt keinen sprachlichen Grund dafuer (was eben meine Frage war), sondern das ist nur dem erzieherischen Element der Compilerentwickler geschuldet, das zunehmend um sich greift, wie mir scheint. (Ob das gut oder schlecht ist, ist ein separates Thema.)


Hier ein Testprogramm, das zwar C-Code enthaelt aber mit g++ uebersetzt wird:

Code: Alles auswählen

:-Q cat b.c
#include <stdio.h>

int
main(void)
{
        printf("1\n");
        goto there;
        printf("2\n");
        int i = 0;
        printf("i:%d\n", i);
        printf("3\n");
there:
        printf("4\n");

        return 0;
}

:-Q CC=g++ make b
g++     b.c   -o b
b.c: In function ‘int main()’:
b.c:12:1: error: jump to label ‘there’ [-fpermissive]
b.c:7:7: error:   from here [-fpermissive]
b.c:9:6: error:   crosses initialization of ‘int i’
make: *** [b] Error 1

:-Q 
Mit `switch' hat die ganze Sache nur soviel zu tun, dass `switch' intern als Gotos zu den Case-Labels realisiert wird.



Btw @eggy: 16-Zeichen breite Tabs? 8O ... Ist das der neue Ansatz um sich tief verschachtelten Code abzugewoehnen? :mrgreen:
Use ed once in a while!

eggy
Beiträge: 3331
Registriert: 10.05.2008 11:23:50

Re: error: jump to case label [gelöst]

Beitrag von eggy » 03.09.2021 17:43:46

Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 17:17:18
Btw @eggy: 16-Zeichen breite Tabs? 8O ... Ist das der neue Ansatz um sich tief verschachtelten Code abzugewoehnen? :mrgreen:
ähm ja ähm ... ka was da nun wieder passiert ist ... liegt wohl am Thema :roll: :mrgreen:

JTH
Moderator
Beiträge: 3023
Registriert: 13.08.2008 17:01:41
Wohnort: Berlin

Re: error: jump to case label

Beitrag von JTH » 04.09.2021 12:13:40

Ich will auch nochmal :D Hatte gestern Mittag schon angefangen, hier was dazu zu schreiben, aber ihr wart bis zum Abend viel zu fleißig und habt das meiste vorweggenommen ;)

Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 17:17:18
Wie dem auch sei. Mit meinem ``ich glaube, ich uebersehe etwas'' meine ich den Code von dakuan. Ich hatte nicht verstanden, welchen Grund der Compiler hat, diesen Fehler zu werfen.
[…]
Es gibt keinen sprachlichen Grund dafuer (was eben meine Frage war), […]
Doch, den gibt es tatsächlich. Es ist in C++, aber nicht in C, einfach ausdrücklich verboten, in den Gültigkeitsbereich einer Variablen über deren Deklaration hinweg hineinzuspringen:
https://en.cppreference.com/w/cpp/language/switch#Explanation hat geschrieben: Because transfer of control is not permitted to enter the scope of a variable, if a declaration statement is encountered inside the statement, it has to be scoped in its own compound statement:
Verbirgt sich im ISO-Standard anscheinend im Abschnitt zu Statements / Declaration statement. Da finde ich es aber nicht so leicht verständlich. Ein Hoch auf die Dokumentationen auf cppreference.com, die sind immer sehr hilfreich!

Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 17:17:18
[…] sondern das ist nur dem erzieherischen Element der Compilerentwickler geschuldet, das zunehmend um sich greift, wie mir scheint.
Ich finds mit Blick auf die Fehler durch uninitialisierte Variablen, die es mit C und C++ ja oft gibt, nicht verkehrt, dass so eine Problemquelle hier ausdrücklich nicht nur durch die Compilerentwickler, sondern sogar durch den Sprachstandard ausgeschlossen ist.

Eine – logische – Folge der Regel: Der Compilefehler kommt nur, wenn man mehr als einen Case hat. Denn mit nur einem Case gibt es letztendlich keinen Sprung. Folgendes kompiliert als C++:
https://godbolt.org/z/WeTGY8jh7 hat geschrieben:

Code: Alles auswählen

int foo(int x)
{
    switch (x) {
        case 0:
            int a = 42;
            return a;
    }

    return 0;
}
Mit einem zusätzlichen Case scheitert es dann wieder wie bekannt.

Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 17:17:18
Hier ein Testprogramm, das zwar C-Code enthaelt aber mit g++ uebersetzt wird:
Wenn du den Code mit dem g++ kompilierst, ist es in dem Moment doch C++-Code und wird mit entsprechenden Regeln wie der obigen übersetzt. Gültig ist er ja in beiden Sprachen.

Als C-Code würde dein (noch weiter) vereinfachtest Beispiel mit Überspringen der Variableninitialisierung auch entsprechend ohne obige C++-Regel erfolgreich kompilieren:
https://godbolt.org/z/GfYf3j4hY hat geschrieben:

Code: Alles auswählen

int foo()
{
    goto my_label;
    int x = 7;
my_label:
    return x;
}
Aber da man mit dem Überspringen der Initialisierung undefiniertes Verhalten hat (ist das tatsächlich eins?), macht der Compiler hier beliebiges und unerwartetes draus und aus der Funktion z.B. das Äquivalent eines return 0, siehe verlinkten Assembleroutput.

Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 15:51:54
dakuan hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 15:22:25
Ich denke, der Compiler macht sich das sehr einfach, indem er erstmal alles, was innerhalb des switch() Blocks als ein Scope sieht.
Das ist nunmal das Design von Switch in C/C++ ...
Durch die Tatsache, dass der switch-Block ein eigener Scope ist, lassen sich anscheinend lustige Dinge damit machen: Man kann eine Variable vor dem ersten Case deklarieren:
https://godbolt.org/z/dh96xPsfj hat geschrieben:

Code: Alles auswählen

int foo(int i)
{
    switch (i) {
        int x;
        case 0:
            x = 13;
            return x;
        default:
            x = 42;
            return x;
    }
}
Compiliert erfolgreich als C und C++. Man kann dort allerdings keinen Wert zuweisen, die Zeilen vor dem Case werden nie ausgeführt. Daher würd ich das nicht in ernsthaftem Code benutzen. Ich denke, es verwirrt die meisten Codeleser mehr, als dass es hilft.
Manchmal bekannt als Just (another) Terminal Hacker.

Benutzeravatar
Meillo
Moderator
Beiträge: 8813
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: error: jump to case label

Beitrag von Meillo » 04.09.2021 14:21:42

JTH hat geschrieben: ↑ zum Beitrag ↑
04.09.2021 12:13:40
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 17:17:18
Wie dem auch sei. Mit meinem ``ich glaube, ich uebersehe etwas'' meine ich den Code von dakuan. Ich hatte nicht verstanden, welchen Grund der Compiler hat, diesen Fehler zu werfen.
[…]
Es gibt keinen sprachlichen Grund dafuer (was eben meine Frage war), […]
Doch, den gibt es tatsächlich. Es ist in C++, aber nicht in C, einfach ausdrücklich verboten, in den Gültigkeitsbereich einer Variablen über deren Deklaration hinweg hineinzuspringen:
https://en.cppreference.com/w/cpp/language/switch#Explanation hat geschrieben: Because transfer of control is not permitted to enter the scope of a variable, if a declaration statement is encountered inside the statement, it has to be scoped in its own compound statement:
Verbirgt sich im ISO-Standard anscheinend im Abschnitt zu Statements / Declaration statement. Da finde ich es aber nicht so leicht verständlich. Ein Hoch auf die Dokumentationen auf cppreference.com, die sind immer sehr hilfreich!
Vielen Dank fuer diese Info! ... und auch ueberhaupt fuer deinen ausfuehrlichen Post und die Recherche. :THX: (Zur Erklaerung: Ich programmiere nur C. Mir war nicht bewusst, dass es auf dieser tiefen Ebene auch solche Unterschiede gibt.)


Zu:
JTH hat geschrieben: ↑ zum Beitrag ↑
04.09.2021 12:13:40
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 17:17:18
[…] sondern das ist nur dem erzieherischen Element der Compilerentwickler geschuldet, das zunehmend um sich greift, wie mir scheint.
Ich finds mit Blick auf die Fehler durch uninitialisierte Variablen, die es mit C und C++ ja oft gibt, nicht verkehrt, dass so eine Problemquelle hier ausdrücklich nicht nur durch die Compilerentwickler, sondern sogar durch den Sprachstandard ausgeschlossen ist.

Eine – logische – Folge der Regel: Der Compilefehler kommt nur, wenn man mehr als einen Case hat. Denn mit nur einem Case gibt es letztendlich keinen Sprung. Folgendes kompiliert als C++:
https://godbolt.org/z/WeTGY8jh7 hat geschrieben:

Code: Alles auswählen

int foo(int x)
{
    switch (x) {
        case 0:
            int a = 42;
            return a;
    }

    return 0;
}
Mit einem zusätzlichen Case scheitert es dann wieder wie bekannt.
Und:
JTH hat geschrieben: ↑ zum Beitrag ↑
04.09.2021 12:13:40
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 15:51:54
dakuan hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 15:22:25
Ich denke, der Compiler macht sich das sehr einfach, indem er erstmal alles, was innerhalb des switch() Blocks als ein Scope sieht.
Das ist nunmal das Design von Switch in C/C++ ...
Durch die Tatsache, dass der switch-Block ein eigener Scope ist, lassen sich anscheinend lustige Dinge damit machen: Man kann eine Variable vor dem ersten Case deklarieren:
https://godbolt.org/z/dh96xPsfj hat geschrieben:

Code: Alles auswählen

int foo(int i)
{
    switch (i) {
        int x;
        case 0:
            x = 13;
            return x;
        default:
            x = 42;
            return x;
    }
}
Compiliert erfolgreich als C und C++. Man kann dort allerdings keinen Wert zuweisen, die Zeilen vor dem Case werden nie ausgeführt. Daher würd ich das nicht in ernsthaftem Code benutzen. Ich denke, es verwirrt die meisten Codeleser mehr, als dass es hilft.
Diese zwei Beispiele, die du hier anfuehrst, zeigen aus meiner Sicht, dass es zwar eine nette Idee ist, dieses Problem mit dem Sprachstandard loesen zu wollen, sich dies damit in der Realitaet aber damit gar nicht erreichen laesst. Das Design von Switch laesst es nicht zu, dass man hier formal ansetzen kann, weil das Switch dafuer nicht strukturiert genug ist. Man kann es dann so sehen, dass es gut ist, wenn man wenigstens die haeufigsten Faelle abfaengt, oder man kann es schlecht finden, dass man den Sprachstandard aufblaeht ohne dass man das Problem damit wirklich loesen kann. Fuer mich wirkt es unstimmig, mit so harten Mitteln ein Problem anzugehen, das so weich ist. Ich finde, dass man die (aus heutiger Sicht dominierenden) Schwaechen des Switch und seines Design akzeptieren muss, statt sich schoenzureden, dass es anders waere oder man das formal reparieren koennte ohne sein Design zu aendern. Wenn man es richtig haette loesen wollen, dann haette man ein anderes Switch gebraucht. Aber nun gut, diese Zerrissenheit, zwischen C-Kompatibilitaet wahren und gegen dessen unerwuenschte Nachteile anzuarbeiten, sind ein Grund warum ich kein C++ programmiere.

Wenn man diese ganzen potenziellen Problemfaelle Warnings umsetzt, dann finde ich das sinnvoll. Wenn man daraus harte Fehler oder halblebige Loesungen im Sprachstandard macht, finde ich das weniger gut. Dein Beispiel zeigt ja schoen, wie man sich weiterhin in den Fuss schiessen kann ... waehrend der Sprachstandard in dem einen (noch nicht mal zwangslaeufig falschen) Fall den Eindruck erweckt, dass man die Probleme formal eingedaemmt haette. Das ist eine Unstimmigkeit, die ich nicht mag, weil ich dann nicht einschaetzen kann was ich zu erwarten habe. Ich werde immer wieder ueberrascht werden weil die Sprache eben unstimmig ist. Darum mag ich C so sehr: Dort weiss ich genau was ich zu erwarten habe, weil C eine so grosse Stimmigkeit im Sprachdesign hat.

JTH hat geschrieben: ↑ zum Beitrag ↑
04.09.2021 12:13:40
Meillo hat geschrieben: ↑ zum Beitrag ↑
03.09.2021 17:17:18
Hier ein Testprogramm, das zwar C-Code enthaelt aber mit g++ uebersetzt wird:
Wenn du den Code mit dem g++ kompilierst, ist es in dem Moment doch C++-Code und wird mit entsprechenden Regeln wie der obigen übersetzt. Gültig ist er ja in beiden Sprachen.
Damit hast du recht.
JTH hat geschrieben: ↑ zum Beitrag ↑
04.09.2021 12:13:40
Als C-Code würde dein (noch weiter) vereinfachtest Beispiel mit Überspringen der Variableninitialisierung auch entsprechend ohne obige C++-Regel erfolgreich kompilieren:
https://godbolt.org/z/GfYf3j4hY hat geschrieben:

Code: Alles auswählen

int foo()
{
    goto my_label;
    int x = 7;
my_label:
    return x;
}
Aber da man mit dem Überspringen der Initialisierung undefiniertes Verhalten hat (ist das tatsächlich eins?), macht der Compiler hier beliebiges und unerwartetes draus und aus der Funktion z.B. das Äquivalent eines return 0, siehe verlinkten Assembleroutput.
Hier machst du einen Fehler weil dein Beispiel *keine* Vereinfachung meines Beispiels ist, sondern einen ganz anderen Fall darstellt, bei dem ich den Fehler auch problemlos nachvollziehen kann. Dein Beispiel muss zurecht angemeckert werden, weil `x' im Return nicht bekannt sein kann, mein Beispiel und dekuans Code haben dieses Problem aber nicht. Sie sind aber von folgendem Schema:

Code: Alles auswählen

int foo()
{
    int y = 1;
    goto my_label;
    int x = 7;
    y = x;
my_label:
    return y;
}
D.h. es gibt keine Zugriffe auf Variablen deren Definition nicht erfolgt ist. Wuerde man den uebersprungenen Code entfernen, dann waere der Rest valide. Schaut man sich den uebersprungenen Code fuer sich genommen an, dann ist er ebenfalls valide.

Dein Beispiel muss vom Compiler abgelehnt werden, weil der Code nicht funktionieren kann; mein Beispiel kann kompiliert werden weil der Code durchaus problemlos funktioniert. (Einen Hinweis auf das *potenzielle* aber nicht zwangslaeufige Problem faende ich schon sinnvoll, aber das ist nebensaechlich.)


Hier nochmal meine Versuche zur Demonstration:

Code: Alles auswählen

:-Q cat c.c
#include <stdio.h>

int main(void)
{
    int y = 4;
    goto my_label;
    int x = 7;
    y = x;
my_label:
    return y;
}

:-Q c99 -Wall -Wextra -pedantic c.c -o c

:-Q ./c ; echo $?
4

:-Q rm c

:-Q g++ -Wall -Wextra -pedantic c.c -o c
c.c: In function ‘int main()’:
c.c:9:1: error: jump to label ‘my_label’ [-fpermissive]
c.c:6:10: error:   from here [-fpermissive]
c.c:7:9: error:   crosses initialization of ‘int x’

:-Q ./c ; echo $?
mksh: ./c: not found
127

:-Q 
Use ed once in a while!

dakuan
Beiträge: 97
Registriert: 28.04.2011 22:09:39

Re: error: jump to case label [gelöst]

Beitrag von dakuan » 04.09.2021 20:19:26

Eine – logische – Folge der Regel: Der Compilefehler kommt nur, wenn man mehr als einen Case hat. Denn mit nur einem Case gibt es letztendlich keinen Sprung. Folgendes kompiliert als C++:
Nicht ganz. In meinem Beispiel hatte ich ja de facto 2 Cases, wo das monatelang funktioniert hatte. Aber nur, weil die Definition im letzten Case statt fand.

Ich meine allerdings mal gelesen zu haben, dass der Compiler die Freiheit hat, die Cases anders anzuordnen. Aber ich glaube das sollten wir jetzt nicht weiter betrachten, Sonst werden wir noch als Erbsenzähler tituliert.

Das dieses Verhalten in der Norm so festgeschrieben ist, war mir allerdings auch neu.
Es ist in C++, aber nicht in C, einfach ausdrücklich verboten, in den Gültigkeitsbereich einer Variablen über deren Deklaration hinweg hineinzuspringen:
Ich stolpere auch immer wieder über diese feinen Unterschiede, besonders wenn ich Jahre alten C Code in C++ Methoden überführe.
Aber nun gut, diese Zerrissenheit, zwischen C-Kompatibilitaet wahren und gegen dessen unerwuenschte Nachteile anzuarbeiten, sind ein Grund warum ich kein C++ programmiere.
Ich bin ja eigentlich auch eher ein C-Programmierer. Aber wenn man eine GUI benötigt und sich mit SDL oder GTK nicht so recht anfreunden kann, muss man sich auch mit C++ beschäftigen. Aber die gefühlt jährlichen Erweiterungen machen C++ irgendwann zu einem unbeherrschbaren Monster. Aber ich muss ja nicht alle Neuerungen mitmachen.
Darum mag ich C so sehr: Dort weiss ich genau was ich zu erwarten habe, weil C eine so grosse Stimmigkeit im Sprachdesign hat.
Ja, C ist ziemlich geradlinig. Die Gegner sagen zwar immer, das man sich damit leicht ins Knie schießen kann, aber wer regelmäßig mit C unterwegs ist, bekommt schnell einen Blick für die Probleme.

Benutzeravatar
Meillo
Moderator
Beiträge: 8813
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: error: jump to case label [gelöst]

Beitrag von Meillo » 04.09.2021 22:15:35

dakuan hat geschrieben: ↑ zum Beitrag ↑
04.09.2021 20:19:26
Eine – logische – Folge der Regel: Der Compilefehler kommt nur, wenn man mehr als einen Case hat. Denn mit nur einem Case gibt es letztendlich keinen Sprung. Folgendes kompiliert als C++:
Nicht ganz. In meinem Beispiel hatte ich ja de facto 2 Cases, wo das monatelang funktioniert hatte. Aber nur, weil die Definition im letzten Case statt fand.
Einfach grossartig! :-D
Use ed once in a while!

Antworten