Wie aktiviere ich die Compiler-Warnung beim Vergleich von char und unsigned char?

Lesezeit: 8 Minuten

Benutzer-Avatar
sagen

Nehmen Sie zum Beispiel den folgenden C-Code:

int main(int argc, char *argv[])
{
    signed char i;
    unsigned char count = 0xFF;

    for (i=0; i<count;i++)
    {
        printf("%x\n", i);
    }
    return 0;
}

Dieser Code läuft in einer Endlosschleife, auch wenn ich ihn wie folgt kompiliere:

# gcc -Wall -Wpedantic -Wconversion -Wsign-compare -Wtype-limits -Wsign-conversion test.c -o test

Kennt jemand ein Compiler-Flag, das vor solchen Problemen warnen sollte?

Nur um das klarzustellen, ich frage nicht, warum eine Endlosschleife kommt, sondern um zu wissen, ob es eine Möglichkeit gibt, dies mit einem Compiler oder einer statischen Analyse zu verhindern.

  • Sind die Optionen -W nicht nur Warnungen? Ihr Code wird für immer ausgeführt, da ein signiertes Zeichen nicht über 127 gehen kann, bevor es umläuft (oder ein undefiniertes Verhalten festlegt), daher kann es Ihre Endbedingung für die Schleife nicht überschreiten.

    – Nicomp

    31. Oktober 2016 um 11:48 Uhr

  • Kommt mir sehr nach einer Hausaufgabenfrage vor. Unnötige Typabweichung in einem so kleinen Programm? Gebrauch von signed char überhaupt?

    – Johannes Bollinger

    31. Oktober 2016 um 11:48 Uhr

  • Vielleicht würde Lint es erkennen.

    – John Colemann

    31. Oktober 2016 um 12:02 Uhr

  • Das signierte Zeichen ist immer kleiner als 255. Daher verlässt es die Schleife nicht.

    – Taha Paksu

    31. Oktober 2016 um 12:16 Uhr

  • Eine Anmerkung zusätzlich zur vorhandenen Antwort: Es ist wahr, dass Sie es nicht können erkennen ein vorzeichenbehafteter Integer-Überlauf, aber mit gcc können Sie verwenden -fwrapv bis mindestens, Vermeiden Sie die UB (Aber Ihr Code ist immer noch defekt, es sei denn, der Compiler verwendet ihn für eine Optimierung, die von erkannt werden kann -wstrict-overflow. Kurz gesagt: Sie können es erkennen, wenn Sie sich dessen bewusst sind (viele Assign-with-Overflow-Check-Implementierungen da draußen), aber AFAIK, wenn Sie es versehentlich tun, dann werden Sie einfach gebissen

    – Adriano Repetti

    31. Oktober 2016 um 13:58 Uhr


Benutzer-Avatar
2501

Das Flag -Wconversion fängt den Fehler nicht ab, da beide Operanden im Vergleich: i<countwerden Sie mithilfe von Integer-Promotions zu int befördert.

Es gibt kein Flag in gcc, das dies abfangen würde.

Abgesehen davon ist das Verhalten Ihres Codes undefiniert, da die Variable i überläuft, wenn sie den Wert hat 0x7F und wird erhöht: i++.

Wenn Sie bis zu einem bestimmten Wert iterieren möchten, stellen Sie sicher, dass der verwendete Typ diesen Wert darstellen kann.

  • Ich weiß, dass die Variable i überläuft, genau das wollte ich erreichen. Ich frage nicht, warum es in einer Endlosschleife läuft und wie es behoben werden kann. Ich würde gerne wissen, ob es eine Möglichkeit gibt, dass der Compiler dieses Problem abfängt.

    – sagen

    31. Oktober 2016 um 11:56 Uhr

  • Danke @ 2501, ich habe die gcc-Manpage für alle Warnmeldungen durchgesehen und konnte keine finden.

    – sagen

    31. Oktober 2016 um 11:59 Uhr


  • @sagi Sie könnten einen statischen Analysator eines Drittanbieters ausprobieren.

    – 2501

    31. Oktober 2016 um 12:01 Uhr

  • clang-analyzer mit obigem gcc-Befehl findet nichts

    – sagen

    31. Oktober 2016 um 12:08 Uhr

  • @usr: Die Tatsache, dass Compiler-Warnungen hier und da auf undefiniertes Verhalten hinweisen, ist zwar an sich wahr, und in diesem Fall wäre eine Warnung am hilfreichsten.

    – Matthias M.

    31. Oktober 2016 um 15:02 Uhr

i ist ein signed chares darüber hinaus inkrementieren SCHAR_MAX hat eine implementierungsdefinierte Wirkung. Die Berechnung i + 1 wird nach der Förderung von durchgeführt i zu int und es läuft nicht über (es sei denn sizeof(int) == 1 und SCHAR_MAX == INT_MAX). Dieser Wert liegt jedoch außerhalb des Bereichs von i und da i hat einen signierten Typ, Entweder ist das Ergebnis implementierungsdefiniert oder es wird ein implementierungsdefiniertes Signal ausgelöst. (C11 6.3.1.3p3 Ganzzahlen mit und ohne Vorzeichen).

Per Definition ist der Compiler die Implementierung, daher wird das Verhalten für jedes spezifische System und auf x86-Architekturen definiert, wo das Speichern des Werts dazu führt, dass die niederwertigen Bits maskiert werden. gcc Beachten Sie, dass der Schleifentest definitiv konstant ist, was ihn zu einer Endlosschleife macht.

Beachten Sie, dass clang erkennt der Dauertest auch nicht, aber clang 3.9.0 wird wenn count wird als deklariert constund es wird eine Warnung ausgegeben, wenn i < count wird durch ersetzt i < 0xffnicht wie gcc.

Keiner der Compiler beschwert sich über das Problem des vorzeichenbehafteten / vorzeichenlosen Vergleichs, da beide Operanden tatsächlich heraufgestuft werden int vor dem Vergleich.

Sie haben hier ein bedeutendes Problem gefunden, das besonders wichtig ist, da einige Codierungskonventionen darauf bestehen, den kleinstmöglichen Typ für alle Variablen zu verwenden, was zu solchen Kuriositäten führt wie int8_t oder uint8_t Loop-Index-Variablen. Solche Entscheidungen sind in der Tat fehleranfällig, und ich habe noch keinen Weg gefunden, den Compiler dazu zu bringen, den Programmierer vor dummen Fehlern wie dem von Ihnen geposteten zu warnen.

  • Sie scheinen über die Addition i + 1 zu sprechen, aber OP verwendet einen Postfix ++ -Operator, der keine ganzzahligen Beförderungen durchführt. Es kommt zu einem Überlauf und ub.

    – 2501

    31. Oktober 2016 um 13:51 Uhr


  • @2501: Ich habe im C11-Standard nach relevanter Sprache gesucht, um dies zu unterstützen, und konnte nichts Schlüssiges finden. Können Sie erläutern, warum Sie davon ausgehen i++ sollte sich anders verhalten aus i += 1 und/oder i = i + 1?

    – chqrlie

    31. Oktober 2016 um 13:54 Uhr

  • Sie gehen davon aus, dass sich postfix ++ genauso verhält wie +=. Das tut es nicht. Präfix ++ tut es, weil es explizit erwähnt wird.

    – 2501

    31. Oktober 2016 um 13:58 Uhr


  • @ 2501: Ich habe dazu eine separate Frage gestellt. Die Antwort verdient genaue Referenzen aus dem Standard.

    – chqrlie

    31. Oktober 2016 um 14:07 Uhr

Benutzer-Avatar
skrtbhtngr

Seit i ist ein signed charsein Wert reicht von -128 to 127 typisch. Wohingegen count ein sein unsigned char wird der Wert zugewiesen 255 (0xFF).

Innerhalb der Schleife, wann i Wert kommt 127 und wieder erhöht wird, erreicht es nie 128 und kommt zu -128 und kommt dann wieder dazu 127 und rollt dann wieder um -128, usw. Der Wert von i wird für immer geringer sein als der Wert von count innerhalb der Schleife und daher kann die Schleife niemals enden!

Dies geschieht aufgrund des Überlaufs des Datentyps und es muss darauf geachtet werden, die Ausdrücke kritisch zu prüfen, bei denen automatische Typumwandlungen stattfinden können, da sie keine Warnung ausgeben.

BEARBEITEN: Aus der GCC-Dokumentation,

-Wconversion: Warnt vor impliziten Konvertierungen, die einen Wert ändern können.

Hier erhalten wir eine Inkonsistenz aufgrund eines Vergleichs und nicht einer Zuordnung.

  • Ich weiß, dass die Variable i überläuft, genau das wollte ich erreichen. Ich frage nicht, warum es in einer Endlosschleife läuft und wie es behoben werden kann. Ich würde gerne wissen, ob es eine Möglichkeit gibt, dass der Compiler dieses Problem abfängt.

    – sagen

    31. Oktober 2016 um 11:57 Uhr

  • Erwähnenswert: Da nicht alle Werte des Zeichens mit Vorzeichen im Zeichen ohne Vorzeichen dargestellt werden können (und umgekehrt), werden beide Operanden des Vergleichsoperators in die nächste int-Kategorie konvertiert, die sie beide aufnehmen kann, nämlich int. Die Vorzeichenerhaltung dieser Konvertierung ist garantiert, so dass man am Ende -128..+127 mit 255 vergleicht, was immer wahr ist.

    – Christoph

    31. Oktober 2016 um 12:01 Uhr

  • @sagi: Da die Typkonvertierung im Bedingungsteil der for-Schleife einen schmaleren Typ (signed char) in einen breiteren Typ (unsigned char) befördert, wird diese Konvertierung nicht vom Compiler gemeldet.

    – skrtbhtngr

    31. Oktober 2016 um 12:06 Uhr


  • Tatsächlich werden beide konvertiert int im Vergleich, aber das ist in diesem Zusammenhang trivial.

    – skrtbhtngr

    31. Oktober 2016 um 13:12 Uhr

  • Wenn diese Antwort für jemanden falsch oder unangemessen erscheint, posten Sie bitte einen Kommentar zusammen mit der Ablehnung!

    – skrtbhtngr

    31. Oktober 2016 um 14:09 Uhr

Benutzer-Avatar
ivan_pozdeev

Es gibt keine Warnung, dies zu fangen weil in diesem Code vom Standpunkt des Compilers nichts fragwürdig ist.

  1. Wie in anderen Antworten erwähnt, wird die Vergleichslinie effektiv behandelt als

    for (i=0; (int)i<(int)count; i++)
    
    • Wie beschrieben in Begründung für International Standard – Programmiersprachen – CAbschnitt 6.3.1.1, wählten sie für implizite Typkonvertierungen beim Vergleichen von Werten den „Werterhaltenden“ Ansatz, da dieser weniger Potenzial für unerwartete Vergleichsergebnisse hat.

      • Beachten Sie, dass Ihr Programm “falsch” läuft speziell weil das Ergebnis des arithmetischen Vergleichs ist das erwartete.
    • Wenn diese wären ints, diese “werterhaltende” Semantik wäre unmöglich zu erreichen – weil an int ist in der Regel ein Hardware-Typ (oder zumindest ist C darauf ausgelegt), und es gibt wenige (wenn überhaupt) Architekturen, die es zulassen, dass ein Operand signiert wird, während der andere unsigniert ist. Das ist der Hauptgrund dafür, dass ein Compiler eine Warnung „Vergleich zwischen signiert und unsigniert“ ausgibt wenn Sie ersetzen chars mit intist hier.

  2. Für die Inkrementoperation gilt: i++,

    • Die gleiche oben verlinkte Begründung erwähnt an einigen Stellen, dass “stiller Überlauf” bei weitem die häufigste und erwartete Semantik ist. Es sogar soll in erster Linie “einen Mangel an Sensibilität in der C-Community für die Unterschiede zwischen vorzeichenbehafteter und vorzeichenloser Arithmetik” verursacht haben! Also auch hier nichts Verdächtiges.

Was letztendlich diese Verwirrung verursacht hat, ist Ihre Unachtsamkeit gegenüber dem int Förderung Semantik. Was das Verhalten des Codes verursacht hat unerwartet für dich (kein schlechtes Gewissen, das wusste ich auch nicht, bevor ich die anderen Antworten gelesen habe!). Doch es stellt sich heraus, es ist für den Standard durchaus zu erwarten – außerdem davon diktiert. “Unkenntnis des Gesetzes ist keine Entschuldigung”, wie sie sagen.

1334810cookie-checkWie aktiviere ich die Compiler-Warnung beim Vergleich von char und unsigned char?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy