Warnungsfreien code schreiben....



  • Hi,

    Also wahrnungsfreien code zu schreiben und das auf höchster Warnstufe ist ja mit Sicherheit sinnvoll um sich vom Compiler so weit wie irgendmöglich unterstützen zu lassen. Allerdings machts mir mein VS2003 Compiler teilweise nicht leicht. Hier mal ein paar Beispiele:

    // Problem
    do
    {
      //...
    } while (TRUE);
    
    // beschwert sich über den konstanten Ausdruck als Bedingung
    
    // Alternative
    BOOL bEndless = TRUE;
    do
    {
    
    } while (bEndless);
    
    // auch nicht wirklich hübsch
    
    // Problem
    char c = 0xE8;
    
    // beschwert sich über verkürzten konstanten Ausdruck??? Ich hätte eigentlich lieber gern eine genaue kopie des Bytes ohne verkürzung nur wie?
    
    // Problem
    InterfaceBasis::ZumUeberschreiben(int param1, int param2, int param3)
    {
      return NOT_SUPPORTED;
    }
    
    // beschwert sich das ich die Parameter nicht verwende. Aber wozu denn auch, die se implementation dient ja nur als abfangmechanismus für nicht implementierte Funktionen.
    
    InterfaceBasis::ZumUeberschreiben(int param1, int param2, int param3)
    {
      param1 = param1;
      param2 = param2;
      param3 = param3;
    
      return NOT_SUPPORTED;
    }
    
    // man oh man. ziemlicher mist und auch nicht ganz ungefährlich
    

    Soviel zu meinen ersten erlebnissen, nur was mache ich Falsch und wie macht mans Besser?

    Vielen dank im voraus
    template



  • Denke damit musst du leben, wenn du auf höchste Warnstufe stellt.

    Er warnt hier überall, wo sich potentielle Gefahren verbergen und Probleme entstehen könnten.



  • Hi,

    höchste Warnstufe finde ich auf jeden Fall sinnvoll!
    In Fällen, wo eine Warning definitiv fehl am Platz ist, würde ich sie
    per Hand ausstellen (#pragma warning()).
    Und Endlosschleifen würde ich mir for(;;) machen.

    Jockel



  • do
    {
      //...
    } while (TRUE);
    // absolut berechtigte Warnung
    
    // Problem
    char c = 0xE8;
    // wenn char signed ist, passt 0xE8 da nit rein, also völlig zu recht gewarnt
    
    // Problem
    InterfaceBasis::ZumUeberschreiben(int param1, int param2, int param3)
    {
      // parameter hier nach void casten
      (void)param1;
      ....
      return NOT_SUPPORTED;
    }
    

  • Mod

    versuch mal vs2005 und erwarte tausende deprecated meldungen... :p

    // Problem
    do
    {
      //...
    } while (TRUE);
    
    // beschwert sich über den konstanten Ausdruck als Bedingung
    
    // Alternative
    BOOL bEndless = TRUE;
    do
    {
    
    } while (bEndless);
    
    // auch nicht wirklich hübsch
    

    machs mit

    for ( ;; ) {}
    

    ist nat. geschmackssache (ich mag die for variante sowieso mehr) - und den compiler machts glücklich.

    // Problem
    char c = 0xE8;
    
    // beschwert sich über verkürzten konstanten Ausdruck??? Ich hätte eigentlich lieber gern eine genaue kopie des Bytes ohne verkürzung nur wie?
    

    hex-literale sind vorzeichenlos, char auf bei vc aber per default vorzeichenbehaftet - hier kommt es also zu einer konvertierung - insofern ist die warnung berechtigt.

    // Problem
    InterfaceBasis::ZumUeberschreiben(int param1, int param2, int param3)
    {
      return NOT_SUPPORTED;
    }
    
    // beschwert sich das ich die Parameter nicht verwende. Aber wozu denn auch, die se implementation dient ja nur als abfangmechanismus für nicht implementierte Funktionen.
    
    InterfaceBasis::ZumUeberschreiben(int param1, int param2, int param3)
    {
      param1 = param1;
      param2 = param2;
      param3 = param3;
    
      return NOT_SUPPORTED;
    }
    
    // man oh man. ziemlicher mist und auch nicht ganz ungefährlich
    

    eine der weniger nützlichen warnungen.
    wenn du bereit bist, ein (void)x; zu schreiben - und dann kann dir evtl. ein 'expression has no effekt' um die ohren fliegen - kannst du genausogut die namen der paramter in der definition auskommentieren.
    aber es gibt noch schlimmerere (4710/4711) z.b. :p oder auch 4668.
    Mein Favorit is 4619: #pragma warning : there is no warning number 'number' - als ob es ein schaden wäre, eine warnung, die nicht existiert, abzuschalten

    /W4 aktiviert übrigens nicht alle warnungen - dafür musst du /Wall verwenden.



  • InterfaceBasis::ZumUeberschreiben(int, int, int)
    {
      return NOT_SUPPORTED;
    }
    


  • Vielen dank für die Tips.

    Also auf das mit der for endlosschleife hätte man kommen können, nagut;)

    Die Idee mit den Parametern nur mit Typen finde ich auch klasse das dürfte auch helfen.

    Bei der Wertzuweisung sehe ich aber sein wirkliches Problem immer noch nicht. Er beschwert sich das er den Wert verkürzt aber das ist überhaupt nicht das was ich von ihm will (und ich hoffe auch nicht das was er tut, sonst hab ich an der stelle wirklich mehr probleme als eine einfache Warnung). Ich will einfach nur das er die char variable für die Zuweisung wie ein Byte ansieht vollkommen unabhängig davon ob das ding nun im späteren verlauf als vorzeichenbehaftet oder nicht angesehen wird, sprich ein

    memcpy(&c, "\xE8", 1);

    und das tut er ja soweit ich weiß auch. dementsprechend verkürzt er ja keinen wert sondern deren interpretation ändert sich von 232 in -24. Das er warnt ist ja auch vollkommen okay, aber warum hat er trotz eines casts immer noch ein problem? Wenn ich sowas schreibe gibts keine Warnung:

    unsigned long l;
    char c = (char)l;

    und da kann es wirklich gut passieren das informationen verloren gehen und sich nicht nur deren interpretation ändert.



  • template schrieb:

    Wenn ich sowas schreibe gibts keine Warnung:

    Weil der Compiler davon ausgeht, dass wenn du schon einen cast verwendest, du auch genau weißt, was du tust.



  • char c = '\xE8';
    

    0xE8 ist vom Typ int. Wenn Du einen int einem char zuweist, kann das gefährlich sein.



  • template schrieb:

    Also auf das mit der for endlosschleife hätte man kommen können, nagut;)

    Nene. Es geht hier nicht darum, eine Technik zu finden, um die Compiler-Warnung auszuhebeln. while( true ) mit break ist manchmal schlechter Stil und lässt sich oft übersichtlicher mit einer anständigen while-Bedingung formulieren. Daher warnt der Compiler dich. Wenn du allerdings der Meinung bist, dass dein Stil in Ordnung geht, dann stell doch diese spezielle Warnung einfach aus. Mit for( ;; ) hast du nichts besser gemacht.



  • Ich finde es komisch das du eine do...while Schleife für das erste Beispiel genommen hast. Macht für mich den Eindruck als du du den Sinn davon nicht verstanden hast.



  • Ja gut, aber warum glaubt mir der compiler dann nicht das ich mit dem hex zu char cast weis was ich tue?

    Ja einen int in einen char zu konvertieren ist möglicherweise verlustbehaftet aber gerade in diesem fall weiß der compiler ja sicher das der Platz ausreicht, weil die Konstante ohne bitverluste in einem Byte unterzubringen ist.

    Naja eigentlich geht es mit unter schon darum den Code warnungsentsprechend umzustellen. Gut ich gebe zu mit einer komplett leeren for schleife ist das ganze ein bischen sauberer als mit dem Risiko das der compiler aus dem while (TRUE) ein

    mov ax, 1
    jnz schleifenanfang

    macht, aber so im großen und ganzen 100%tig hübsch ist die for( ;; ) ja nun rein leserlich auch nicht wirklich.

    Und mir ist durchaus bewusst, dass dass nicht der normale Weg ist eine schleife mit einem break zu beenden aber es geht eben manchmal einfacher:

    do
    {
      /* tue was */
      if (/* tue was anderes */ == 0) break;
      /* tue noch was anderes */
    } while (TRUE);
    
    // anstatt
    
    int stayinloop = 1;
    do
    {
      /* tue was */
      if (/* tue was anderes */ == 0) stayinloop = 0;
      if (stayinloop)
      {
        /* tue noch was anderes */
      }
    } while (stayinloop);
    


  • template schrieb:

    // Problem
    InterfaceBasis::ZumUeberschreiben(int param1, int param2, int param3)
    {
      return NOT_SUPPORTED;
    }
    

    Soweit ich weiss, muß man in C++ keinen Parameternamen angeben. Der GCC meckert hier auch, aber nicht bei folgendem Code:

    InterfaceBasis::ZumUeberschreiben(int, int, int)
    {
      return NOT_SUPPORTED;
    }
    

    Es ist halt nur gefährlich manchmal einen Parameternamen wegzunehmen, weil die Warnung doch ein Anzeichen für einen Bug sein kann. Aber insgesamt halte ich das Verhalten für sinnvoll.



  • tntnet schrieb:

    char c = '\0xE8';
    

    0xE8 ist vom Typ int. Wenn Du einen int einem char zuweist, kann das gefährlich sein.

    Das ist nicht richtig. Character-Literale sind in C++ vom Typ char. In C war das anders.

    Und char c = 0xE8 ist sehr wohl verlustbehaftet, wenn char signed ist, denn 0xE8 ist größer als 0x7F, der größte signed char. Der Standard verspricht keine Bitdarstellung im 2er-Komplement, auch wenn das üblich ist. Eine Darstellung mit Vorzeichen-Bit wäre genauso möglich - wo dann 0xE8 nicht mehr so einfach sinnvoll umgewandelt werden könnte.

    char c = '\0xE8';
    

    sollte AFAIK aber korrekt sein.



  • template schrieb:

    Und mir ist durchaus bewusst, dass dass nicht der normale Weg ist eine schleife mit einem break zu beenden aber es geht eben manchmal einfacher:

    Geht doch nicht darum, sondern um den Fall, das deine Condition niemals eintrifft, die den Break schmeisst.

    Ich hab mir z.B: deshalb angewöhnt bei "endlosschleifen" immer das Abbruchkriterium auf einen Schliess-Request zu setzen. Selbst wenn sich die Applikation aufhängt, kann ich sie dann jederzeit noch schliessen und die Kontrolle behalten. Gegenteiliges ist der Fall, wenn die Applikation nicht mehr reagiert, sich nicht beenden lässt und dann per TM aus dem Speicher entfernt wird.

    Das Selbe gilt übrigens auch für irgendwelche WaitFor*Objects-Aurufe in der WinAPI, etc. Hier habe ich mir ebenfalls angewöhnt, nur noch auf multiple Obejkte zu warten, wobei eines davon immer der Close-Event war, der bei einem Close-Request ausgelöst wurde.


Anmelden zum Antworten