Warum hat das kleinste Int, −2147483648, den Typ „long“? [duplicate]

Lesezeit: 7 Minuten

Benutzeravatar von Arthur Thévenot
Arthur Thevenot

Für ein Schulprojekt muss ich die C-Funktion printf codieren. Die Dinge laufen ziemlich gut, aber es gibt eine Frage, auf die ich keine gute Antwort finden kann, also bin ich hier.

printf("PRINTF(d) \t: %d\n", -2147483648);

sagt mir (gcc -Werror -Wextra -Wall):

   error: format specifies type 'int' but the argument has type 'long'
      [-Werror,-Wformat]
        printf("PRINTF(d) \t: %d\n", -2147483648);
                              ~~     ^~~~~~~~~~~
                              %ld

Aber wenn ich eine int-Variable verwende, läuft alles gut:

int i;

i = -2147483648;
printf("%d", i);

Wieso den?

BEARBEITEN:

Ich habe viele Punkte verstanden, und sie waren sehr interessant. Wie auch immer, denke ich printf verwendet die <stdarg.h> Bibliothek und so, va_arg(va_list ap, type) sollte auch den richtigen Typ zurückgeben. Zum %d und %ioffensichtlich ist der zurückgegebene Typ ein int. Ändert es etwas?

  • Zu Ihrer Zusatzfrage, die ich bereits beantwortet habe, aber dann mein Kommentar dazu gelöscht wurde: va_arg() weiß nicht, welchen Typ das Argument hat, das Sie abrufen möchten. Das müssen Sie wissen, und wenn Sie versuchen, einen anderen Typ abzurufen als den, der als Argument übergeben wurde, ist das ein undefiniertes Verhalten. Dies gilt auch, wenn Sie dies tun printf("%d\n", -2147483648) da das Argument einen Typ hat long aber printf versucht, ein zu holen int.

    – fuz

    19. Januar 2016 um 7:40 Uhr

  • Kein Duplikat. Wenn Sie die akzeptierte Antwort auf die andere Frage lesen, liegt dies an undefiniertem Verhalten, das für den Kontext der Frage spezifisch ist. Bei dieser Frage geht es nicht um undefiniertes Verhalten. Diese Frage bezieht sich auf C und die andere auf C++. Beide Sprachen haben ähnliche Regeln für die Beförderung, aber es kann feine Unterschiede geben. Diese Frage wird mehr zukünftigen Besuchern helfen und hat dies vielleicht bereits getan, wenn die viel höheren Stimmenzahlen ein Hinweis darauf sind.

    – Adrian McCarthy

    1. März 2016 um 19:32 Uhr

  • Auch kein Duplikat der zweiten Frage. Die wichtigste Tatsache ist, dass sie das Literal in Hex angegeben haben, was bedeutet, dass es unsigned int und nicht signed long ist.

    – Adrian McCarthy

    1. März 2016 um 19:34 Uhr

  • @ Adrian McCarthy Sie haben das Literal in Hex angegeben, was bedeutet, dass es unsigned int und nicht signed long ist. Das kann gelesen werden als “Hex-Konstanten sind immer ohne Vorzeichen”. Alte Vorstandard-C-Compiler haben häufig Hex-Konstanten ohne Vorzeichen gemacht, was verwirrend sein könnte. Pro 6.4.4.1 GanzzahlkonstantenAbsatz 5: “Der Typ einer Integer-Konstante ist der erste der entsprechenden Liste, in der ihr Wert dargestellt werden kann.” In diesem Fall, 0x80000000 ist ein unsigned int hier, weil es in eine passt unsigned int ist aber zu groß dafür [signed] int.

    – Andreas Henle

    17. Juli 2018 um 19:48 Uhr


  • @AdrianMcCarthy Ich habe meinen Kommentar umgeschrieben, um klarzustellen, dass ich nur versucht habe, ihn zu klären, und meinen vorherigen Kommentar gelöscht

    – Andreas Henle

    17. Juli 2018 um 19:49 Uhr

Benutzeravatar von fuz
fuz

In C, -2147483648 ist keine ganzzahlige Konstante. 2147483648 eine ganzzahlige Konstante ist, und - ist nur ein unärer Operator, der darauf angewendet wird und einen konstanten Ausdruck ergibt. Der Wert von 2147483648 passt nicht in eine int (es ist eins zu groß, 2147483647 ist typischerweise die größte ganze Zahl) und somit hat die ganzzahlige Konstante einen Typ long, was das von Ihnen beobachtete Problem verursacht. Wenn Sie die untere Grenze für eine nennen möchten intverwenden Sie entweder das Makro INT_MIN aus <limits.h> (der tragbare Ansatz) oder vermeiden Sie es sorgfältig zu erwähnen 2147483648:

printf("PRINTF(d) \t: %d\n", -1 - 2147483647);

Benutzeravatar von rici
rici

Das Problem ist, dass -2147483648 ist kein Integer-Literal. Es ist ein Ausdruck, der aus dem unären Negationsoperator besteht - und die Ganzzahl 2147483648das ist zu groß, um ein zu sein int wenn ints sind 32 Bit. Da der Compiler eine entsprechend große vorzeichenbehaftete Ganzzahl zur Darstellung auswählt 2147483648 vor Anwendung des Negationsoperators ist der Typ des Ergebnisses größer als an int.

Wenn Sie wissen, dass Ihre ints sind 32 Bit und möchten die Warnung vermeiden, ohne die Lesbarkeit zu beeinträchtigen, verwenden Sie eine explizite Umwandlung:

printf("PRINTF(d) \t: %d\n", (int)(-2147483648));

Das ist definiertes Verhalten auf einem 2er-Komplement-Rechner mit 32-Bit ints.

Verwenden Sie für eine erhöhte theoretische Portabilität INT_MIN anstelle der Nummer, und teilen Sie uns mit, wo Sie eine Nicht-2er-Komplement-Maschine zum Testen gefunden haben.


Um es klar zu sagen, dieser letzte Absatz war teilweise ein Witz. INT_MIN ist definitiv der richtige Weg, wenn Sie “die Kleinsten” meinen int“, Weil int variiert in der Größe. Es gibt zum Beispiel noch viele 16-Bit-Implementierungen. Ausschreiben -231 ist nur sinnvoll, wenn Sie auf jeden Fall immer genau diesen Wert meinen, in diesem Fall würden Sie wahrscheinlich einen Typ mit fester Größe wie verwenden int32_t Anstatt von int.

Vielleicht möchten Sie eine Alternative zum Schreiben der Zahl in Dezimalzahlen, um sie für diejenigen klarer zu machen, die den Unterschied zwischen nicht bemerken 2147483648 und 2174483648aber Sie müssen vorsichtig sein.

Wie oben erwähnt, auf einer 32-Bit-2er-Komplement-Maschine, (int)(-2147483648) wird nicht überlaufen und ist daher wohldefiniert, weil -2147483648 wird als breiter vorzeichenbehafteter Typ behandelt. Gleiches gilt jedoch nicht für (int)(-0x80000000). 0x80000000 wird als behandelt unsigned int (da es in die vorzeichenlose Darstellung passt); -0x80000000 ist wohldefiniert (aber die - hat keine Wirkung, wenn int ist 32 Bit) und die Umwandlung der resultierenden unsigned int 0x80000000 zu int beinhaltet einen Überlauf. Um den Überlauf zu vermeiden, müssten Sie die Hex-Konstante in einen vorzeichenbehafteten Typ umwandeln: (int)(-(long long)(0x80000000)).

Ebenso müssen Sie vorsichtig sein, wenn Sie den Operator für die linke Verschiebung verwenden möchten. 1<<31 ist undefiniertes Verhalten auf 32-Bit-Rechnern mit 32-Bit (oder kleiner) ints; es wird nur zu 2 ausgewertet31 wenn int ist mindestens 33 Bit, weil Linksverschiebung durch k Bits ist nur dann wohldefiniert, wenn k ist strikt kleiner als die Anzahl der Bits ohne Vorzeichen des ganzzahligen Typs des linken Arguments.

1LL<<31 ist sicher, da long long int muss vertretungsfähig sein 263-1, also muss seine Bitgröße größer als 32 sein. Also das Formular

(int)(-(1LL<<31))

ist wahrscheinlich am lesbarsten. YMMV.


Für alle vorbeikommenden Pedanten ist diese Frage mit C gekennzeichnet, und der neueste C-Entwurf (n1570.pdf) sagt, in Bezug auf E1 << E2wo E1 einen vorzeichenbehafteten Typ hat, dass der Wert nur dann definiert ist, wenn E1 ist nichtnegativ und E1 × 2E2 “ist im Ergebnistyp darstellbar”. (§6.5.7 Abs. 4).

Das ist anders als in C++, wo die Anwendung des Linksverschiebungsoperators if definiert ist E1 ist nichtnegativ und E1 × 2E2 „ist repräsentativ
im entsprechenden vorzeichenlosen Typ des Ergebnistyps“ (§5.8 Abs. 2, Hervorhebung hinzugefügt).

In C++ ist gemäß dem neuesten Entwurfsstandard die Umwandlung eines ganzzahligen Werts in einen vorzeichenbehafteten ganzzahligen Typ implementierungsdefiniert wenn der Wert nicht im Zieltyp darstellbar ist (§4.7 Abs. 3). Der entsprechende Absatz des C-Standards – §6.3.1.3 Abs. 3 — besagt, dass “entweder das Ergebnis implementierungsdefiniert ist oder ein implementierungsdefiniertes Signal ausgelöst wird”.)

  • Die herkömmliche Art zu definieren INT_MIN ist #define INT_MIN (-(INT_MAX)-1)(was das von Ihnen beschriebene Problem vermeidet, das Negativ eines Long zu nehmen. (Hinweis)

    – abelenky

    11. Januar 2016 um 23:33 Uhr


  • @abelenky: Ich weiß, dass es so ist INT_MIN ist herkömmlich definiert auf Zweierkomplementmaschinen. Ansonsten halte ich es für sinnvoll, es als zu definieren (-INT_MAX) weil es sinnvoll ist, die Nummer 2147483647 nur einmal einzugeben. Aber es wurde (vernünftigerweise) vorgeschlagen, printf mit aufzurufen -2147483647-1 ist ein bisschen seltsam. Jetzt, wo wir diese Diskussion geführt haben, wissen wir natürlich, warum Sie das tun müssen. Ich habe nur vorgeschlagen, dass Sie durch die Verwendung einer expliziten Umwandlung die wörtliche Zahl in der Form schreiben können, in der sie normalerweise von denen geschrieben würde, die weder Compiler noch Sprachanwälte sind 🙂

    – Rici

    11. Januar 2016 um 23:40 Uhr

1424840cookie-checkWarum hat das kleinste Int, −2147483648, den Typ „long“? [duplicate]

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

Privacy policy