Warum ist 0 < -0x80000000?

Lesezeit: 7 Minuten

Benutzeravatar von Jayesh Bhoi
Jayesh Bhoi

Ich habe unten ein einfaches Programm:

#include <stdio.h>

#define INT32_MIN        (-0x80000000)

int main(void) 
{
    long long bal = 0;

    if(bal < INT32_MIN )
    {
        printf("Failed!!!");
    }
    else
    {
        printf("Success!!!");
    }
    return 0;
}

Die Bedingung if(bal < INT32_MIN ) ist immer wahr. Wie ist es möglich?

Es funktioniert gut, wenn ich das Makro ändere zu:

#define INT32_MIN        (-2147483648L)

Kann jemand auf das Problem hinweisen?

  • Wie viel ist CHAR_BIT * sizeof(int)?

    – 5gon12eder

    9. Dezember 2015 um 15:37 Uhr

  • Haben Sie versucht, bal auszudrucken?

    – Ryan Fitzpatrick

    9. Dezember 2015 um 15:39 Uhr

  • IMHO ist das Interessantere, dass es wahr ist nur zum -0x80000000aber falsch für -0x80000000L, -2147483648 und -2147483648L (gcc 4.1.2), also ist die Frage: Warum ist das int-Literal -0x80000000 anders als das int-Literal -2147483648?

    – Andreas Fester

    9. Dezember 2015 um 15:41 Uhr


  • @Bathsheba Ich führe gerade ein Programm auf dem Online-Compiler aus tutorialspoint.com/codingground.htm

    – Jayesh Bhoi

    9. Dezember 2015 um 15:42 Uhr

  • Wenn Sie das jemals bemerkt haben (einige Inkarnationen von) <limits.h> definiert INT_MIN wie (-2147483647 - 1)jetzt weißt du warum.

    – zol

    9. Dezember 2015 um 18:51 Uhr


Benutzeravatar von Lundin
Lundin

Das ist ziemlich subtil.

Jedes Integer-Literal in Ihrem Programm hat einen Typ. Welche Art es hat, regelt eine Tabelle in 6.4.4.1:

Suffix      Decimal Constant    Octal or Hexadecimal Constant

none        int                 int
            long int            unsigned int
            long long int       long int
                                unsigned long int
                                long long int
                                unsigned long long int

Wenn eine Literalzahl nicht in den Standardwert passt int Typ, wird der nächstgrößere Typ versucht, wie in der obigen Tabelle angegeben. Für reguläre dezimale Integer-Literale sieht es also so aus:

  • Versuchen int
  • Wenn es nicht passt, versuchen Sie es long
  • Wenn es nicht passt, versuchen Sie es long long.

Hex-Literale verhalten sich jedoch anders! Wenn das Literal nicht in einen vorzeichenbehafteten Typ wie z intwird es zuerst versuchen unsigned int bevor Sie größere Typen ausprobieren. Sehen Sie den Unterschied in der obigen Tabelle.

Auf einem 32-Bit-System also Ihr Literal 0x80000000 ist vom Typ unsigned int.

Dies bedeutet, dass Sie die unäre anwenden können - -Operator für das Literal, ohne das implementierungsdefinierte Verhalten aufzurufen, wie Sie es sonst beim Überlauf einer vorzeichenbehafteten Ganzzahl tun würden. Stattdessen erhalten Sie den Wert 0x80000000ein positiver Wert.

bal < INT32_MIN ruft die üblichen arithmetischen Konvertierungen und das Ergebnis des Ausdrucks auf 0x80000000 wird gefördert von unsigned int zu long long. Der Wert 0x80000000 bleibt erhalten und 0 ist kleiner als 0x80000000, daher das Ergebnis.

Wenn Sie das Literal durch ersetzen 2147483648L Sie verwenden die Dezimalschreibweise und daher wählt der Compiler nicht aus unsigned intsondern versucht eher, es in a einzupassen long. Auch das Suffix L sagt, dass Sie a wollen long wenn möglich. Das L-Suffix hat tatsächlich ähnliche Regeln, wenn Sie die erwähnte Tabelle in 6.4.4.1 weiterlesen: wenn die Nummer nicht in die angeforderte passt longwas im 32-Bit-Fall nicht der Fall ist, gibt Ihnen der Compiler eine long long wo es gut passen wird.

  • “… Ersetzen Sie das Literal durch -2147483648L, Sie erhalten explizit einen Long, der signiert ist.” Hmm, in einem 32-Bit long System 2147483648Lpasst nicht in a longso wird es long long, dann das - angewendet wird – dachte ich jedenfalls.

    – chux – Wiedereinsetzung von Monica

    9. Dezember 2015 um 16:05 Uhr

  • @ASH Weil die maximale Anzahl, die ein int haben kann, dann ist 0x7FFFFFFF. Versuch es selber: #include <limits.h> printf("%X\n", INT_MAX);

    – Ludin

    9. Dezember 2015 um 16:15 Uhr


  • @ASH Verwechseln Sie die hexadezimale Darstellung von Integer-Literalen im Quellcode nicht mit der zugrunde liegenden binären Darstellung einer vorzeichenbehafteten Zahl. Das wörtliche 0x7FFFFFFF wenn im Quellcode geschrieben ist immer eine positive Zahl, aber Ihre int Variable kann natürlich rohe Binärzahlen bis zum Wert 0xFFFFFFFF enthalten.

    – Ludin

    9. Dezember 2015 um 16:22 Uhr

  • @ASCHE ìnt n = 0x80000000 erzwingt eine Konvertierung vom vorzeichenlosen Literal in einen vorzeichenbehafteten Typ. Was passiert, hängt von Ihrem Compiler ab – es ist ein implementierungsdefiniertes Verhalten. In diesem Fall hat es sich entschieden, das ganze Literal in zu zeigen int, Überschreiben des Vorzeichenbits. Auf anderen Systemen ist es möglicherweise nicht möglich, den Typ darzustellen, und Sie rufen ein undefiniertes Verhalten auf – das Programm kann abstürzen. Sie erhalten das gleiche Verhalten, wenn Sie dies tun int n=2147483648; Es hat also überhaupt nichts mit der Hex-Notation zu tun.

    – Ludin

    9. Dezember 2015 um 16:52 Uhr


  • Die Erklärung dafür, wie unär - auf vorzeichenlose Ganzzahlen angewendet wird, könnte etwas erweitert werden. Ich war immer davon ausgegangen (obwohl ich mich glücklicherweise nie darauf verlassen habe), dass vorzeichenlose Werte zu vorzeichenbehafteten Werten “befördert” würden oder dass das Ergebnis möglicherweise undefiniert wäre. (Ehrlich gesagt sollte es ein Kompilierungsfehler sein; was bedeutet - 3u auch gemeint?)

    – Kyle Strand

    11. Dezember 2015 um 20:04 Uhr

Benutzeravatar von Bathsheba
Bathseba

0x80000000 ist ein unsigned Literal mit dem Wert 2147483648.

Anwenden des unären Minus darauf still gibt Ihnen einen vorzeichenlosen Typ mit einem Wert ungleich Null. (Tatsächlich für einen Wert ungleich Null xder Wert, den Sie am Ende haben, ist UINT_MAX - x + 1.)

Vlad aus Moskaus Benutzer-Avatar
Vlad aus Moskau

Dieses Integer-Literal 0x80000000 Typ hat unsigned int.

Nach C-Standard (6.4.4.1 Integer-Konstanten)

5 Der Typ einer Integer-Konstante ist der erste der entsprechenden Liste, in der ihr Wert dargestellt werden kann.

Und diese ganzzahlige Konstante kann durch den Typ von dargestellt werden unsigned int.

Also dieser Ausdruck

-0x80000000 hat das gleiche unsigned int Typ. Außerdem hat es den gleichen Wert
0x80000000 in der Zweierkomplementdarstellung, die auf folgende Weise berechnet wird

-0x80000000 = ~0x80000000 + 1 => 0x7FFFFFFF + 1 => 0x80000000

Dies hat einen Nebeneffekt, wenn Sie zum Beispiel schreiben

int x = INT_MIN;
x = abs( x );

Das Ergebnis wird wieder sein INT_MIN.

Also in diesem Zustand

bal < INT32_MIN

da wird verglichen 0 mit ohne Vorzeichen Wert 0x80000000 nach den Regeln der üblichen arithmetischen Konvertierung in den Typ long long int umgewandelt.

Es ist offensichtlich, dass 0 kleiner als ist 0x80000000.

Benutzeravatar von dbush
dbusch

Die numerische Konstante 0x80000000 ist vom Typ unsigned int. Wenn wir nehmen -0x80000000 und machen Sie 2s Komplimente dazu, erhalten wir Folgendes:

~0x80000000 = 0x7FFFFFFF
0x7FFFFFFF + 1 = 0x80000000

So -0x80000000 == 0x80000000. Und vergleichen (0 < 0x80000000) (seit 0x80000000 ist unsigned) ist wahr.

chux – Stellt Monicas Benutzeravatar wieder her
Chux – Wiedereinsetzung von Monica

Ein Punkt der Verwirrung tritt beim Denken auf - ist Teil der numerischen Konstante.

Im folgenden Code 0x80000000 ist die numerische Konstante. Seine Art wird nur davon bestimmt. Das - wird danach angewendet und ändert den Typ nicht.

#define INT32_MIN        (-0x80000000)
long long bal = 0;
if (bal < INT32_MIN )

Roh schlichte numerische Konstanten sind positiv.

Wenn es sich um eine Dezimalzahl handelt, ist der zugewiesene Typ der erste Typ, der sie enthält: int, long, long long.

Wenn die Konstante oktal oder hexadezimal ist, erhält sie den ersten Typ, der sie enthält: int, unsigned, long, unsigned long, long long, unsigned long long.

0x80000000erhält auf dem System von OP den Typ von unsigned oder unsigned long. In jedem Fall handelt es sich um einen unsignierten Typ.

-0x80000000 ist auch ein Wert ungleich Null und da es sich um einen vorzeichenlosen Typ handelt, ist er größer als 0. Wenn Code das mit a vergleicht long longdas Werte werden auf den 2 Seiten des Vergleichs nicht verändert, also 0 < INT32_MIN ist wahr.


Eine alternative Definition vermeidet dieses merkwürdige Verhalten

#define INT32_MIN        (-2147483647 - 1)

Lassen Sie uns für eine Weile im Fantasieland spazieren gehen, wo int und unsigned sind 48-Bit.

Dann 0x80000000 passt int und so ist der Typ int. -0x80000000 ist dann eine negative Zahl und das Ergebnis des Ausdrucks ist anders.

[Back to real-word]

Seit 0x80000000 passt in einen vorzeichenlosen Typ vor einem vorzeichenbehafteten Typ, da er nur größer als ist some_signed_MAX doch drinnen some_unsigned_MAXes ist ein unsignierter Typ.

Benutzeravatar der Community
Gemeinschaft

C hat eine Regel, die das Integer-Literal sein darf signed oder unsigned kommt drauf an ob es passt signed oder unsigned (Ganzzahlförderung). Auf einen 32-Bit-Maschine das Literal 0x80000000 wird sein unsigned. Zweierkomplement von -0x80000000 ist 0x80000000 auf einem 32-Bit-Rechner. Daher der Vergleich bal < INT32_MIN ist zwischen signed und unsigned und vor dem Vergleich nach der C-Regel unsigned int wird umgewandelt in long long.

C11: 6.3.1.8/1:

[…] Andernfalls, wenn der Typ des Operanden mit vorzeichenbehafteter Ganzzahl alle Werte des Typs des Operanden mit vorzeichenloser Ganzzahl darstellen kann, wird der Operand mit vorzeichenloser Ganzzahl in den Typ des Operanden mit vorzeichenbehafteter Ganzzahl konvertiert.

Deswegen, bal < INT32_MIN ist immer true.

1426290cookie-checkWarum ist 0 < -0x80000000?

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

Privacy policy