Warum gibt "sizeof (a? True: false)" eine Ausgabe von vier Bytes aus?


133

Ich habe einen kleinen Code über den sizeofOperator mit dem ternären Operator:

#include <stdio.h>
#include <stdbool.h>

int main()
{
    bool a = true;
    printf("%zu\n", sizeof(bool));  // Ok
    printf("%zu\n", sizeof(a));     // Ok
    printf("%zu\n", sizeof(a ? true : false)); // Why 4?
    return 0;
}

Ausgabe ( GCC ):

1
1
4 // Why 4?

Aber hier,

printf("%zu\n", sizeof(a ? true : false)); // Why 4?

Der ternäre Operator gibt den booleanTyp zurück und die Größe des boolTyps ist 1Byte in C.

Dann warum sizeof(a ? true : false)eine Leistung von vier Bytes geben?


39
sizeof(true)und sizeof(false)ist auch 4: ide.geeksforgeeks.org/O5jvuN
tkausl

7
Die interessantere Frage hier wäre, warum diese Implementierung "inkonsistent" ist, da sie offensichtlich definiert _Bool, Größe 1 zu haben, aber nicht trueund false. Aber der Standard hat dazu nichts zu sagen, soweit ich das beurteilen kann.

12
@FelixPalmen gleichen Grund warum gegeben char a; sizeof(a) == 1und sizeof('a') == sizeof(int)(in C). Es geht nicht um die Implementierung, es geht um die Sprache.
n. 'Pronomen' m.

10
Haben Sie versucht zu drucken sizeof(true)? Vielleicht wird es die Ausdünnung etwas klarer machen (insbesondere wird es offensichtlich, dass der ternäre Operator ein roter Hering ist).
n. 'Pronomen' m.

4
@FelixPalmen trueist #defined von 1, stdbool.halso ja, dies ist die wörtliche Definition.
n. 'Pronomen' m.

Antworten:


223

Es ist, weil du hast #include <stdbool.h>. Dieser Header definiert Makros true und falsesein 1und 0, also sieht Ihre Aussage folgendermaßen aus:

printf("%zu\n", sizeof(a ? 1 : 0)); // Why 4?

sizeof(int) ist 4 auf Ihrer Plattform.


21
"Es liegt daran, dass Sie #include <stdbool.h> haben." Nein, das ist es nicht. sizeof(a ? (uint8_t)1 : (uint8_t)0);würde auch ein Ergebnis von 4 ergeben. Die ganzzahlige Förderung der ?:Operanden ist hier der wichtige Teil, nicht die Größe von trueund false.
Lundin

9
@Lundin: Beides ist wichtig. Wie geschrieben, ist der Typ bereits intohne Werbung. Der Grund, warum Sie es nicht "reparieren" können, sind die Standardaktionen.
R .. GitHub STOP HELPING ICE

5
@PeterSchneider Dies ist nicht C ++. Dies ist C. In C ++ trueund falseist nicht Makros; Sie sind Schlüsselwörter. Sie sind nicht als 1und definiert 0, sondern als wahre und falsche Werte des boolTyps.
Justin

5
@PeterSchneider Nein, du hast heute etwas über C gelernt. Verwechseln Sie nicht die beiden Sprachen. In C ++ sizeof(true)ist 1. Demo .
Rakete1111

1
Stimmt, verwechselt es. Hatte nicht sorgfältig gelesen und wurde von cppreference-link irregeführt. Meine Schuld, danke. Aber ich habe trotzdem dieses Gefühl in Bezug auf C ++.
Peter Schneider

66

Hier ternärer Operator-Rückgabetyp boolean,

OK, da steckt noch mehr dahinter!

In C ist das Ergebnis dieser ternären Operation vom Typ int. [Anmerkungen unten (1,2)]

Daher ist das Ergebnis dasselbe wie der Ausdruck sizeof(int)auf Ihrer Plattform.


Anmerkung 1: Zitieren C11, Kapitel §7.18,Boolean type and values <stdbool.h>

[....] Die verbleibenden drei Makros eignen sich zur Verwendung in #ifVorverarbeitungsanweisungen. Sie sind

true

die sich auf die ganzzahlige Konstante 1 ausdehnt,

false

die auf die ganzzahlige Konstante 0 erweitert, [....]

Anmerkung 2: Für den bedingten Operator Kapitel §6.5.15 ( Hervorhebung meiner )

Der erste Operand wird ausgewertet; Es gibt einen Sequenzpunkt zwischen seiner Auswertung und der Auswertung des zweiten oder dritten Operanden (je nachdem, welcher ausgewertet wird). Der zweite Operand wird nur ausgewertet, wenn der erste ungleich 0 ist; der dritte Operand wird nur ausgewertet, wenn der erste gleich 0 ist; das Ergebnis ist der Wert des zweiten oder dritten Operanden (je nachdem, welcher Wert ausgewertet wird), [...]

und

Wenn sowohl der zweite als auch der dritte Operand einen arithmetischen Typ haben, ist der Ergebnistyp der Ergebnistyp, der durch die üblichen arithmetischen Konvertierungen bestimmt würde, wenn sie auf diese beiden Operanden angewendet würden. [....]

Daher ist das Ergebnis vom Typ Integer, und aufgrund des Wertebereichs sind die Konstanten genau vom Typ int.

Ein allgemeiner Rat int main()sollte jedoch besser sein int main (void), wirklich standardkonform zu sein.


@ user694733 umm..warum nicht? <stdbool.h>definiert die MACROS als vom Typ int.. ist das falsch?
Sourav Ghosh

@BasileStarynkevitch OK, ich sehe, dass dies jetzt in der Tat falsch scheint, jetzt aktualisiert.
Sourav Ghosh

58

Der ternäre Operator ist ein roter Hering.

    printf("%zu\n", sizeof(true));

druckt 4 (oder was auch sizeof(int)immer auf Ihrer Plattform ist).

Im Folgenden wird davon ausgegangen, dass dies boolein Synonym für charoder ein ähnlicher Typ der Größe 1 ist und intgrößer als ist char.

Der Grund warum sizeof(true) != sizeof(bool)und sizeof(true) == sizeof(int)ist einfach, weil truees kein Ausdruck des Typs ist bool. Es ist ein Ausdruck des Typs int. Es ist #defined wie 1in stdbool.h.

boolIn C gibt es überhaupt keine Werte vom Typ . Jeder solche Wert wird sofort gefördert int, auch wenn er als Argument verwendet wird sizeof. Bearbeiten: Dieser Absatz ist nicht wahr, Argumente, zu denen sizeofnicht befördert werden soll int. Dies hat jedoch keinen Einfluss auf die Schlussfolgerungen.


Gute Antwort. Nachdem ich die derzeit am besten bewertete Antwort gelesen hatte, dachte ich, dass alle Aussagen mit 4 bewertet werden sollten. Dies klärte die Dinge auf. +1
Pedro A

5
Ist nicht (bool)1ein Wert vom Typ bool?
Ben Voigt

printf("%u\n", sizeof((char) 1));druckt 1auf meiner Plattform, während printf("%u\n", sizeof(1));druckt 4. Bedeutet dies nicht, dass Ihre Aussage "Jeder solche Wert wird sofort zu int befördert, auch wenn er als Argument für sizeof verwendet wird" falsch ist?
JonatanE

Dies beantwortet die Frage nicht wirklich. Die Größe und Art von trueetc spielt im Fall von keine Rolle, ?:da die Ganzzahl intsowieso hochgestuft wird. Das heißt, die Antwort sollte sich mit der Frage befassen, warum ?: es sich um einen roten Hering handelt.
Lundin

6
Ich denke, die Antwort spricht das Problem bestmöglich an. Sie können es gerne ablehnen oder verbessern.
n. 'Pronomen' m.

31

In Bezug auf den Booleschen Typ in C.

Ein boolescher Typ wurde ziemlich spät in der C-Sprache eingeführt, im Jahr 1999. Zuvor hatte C keinen booleschen Typ, sondern wurde intfür alle booleschen Ausdrücke verwendet. Daher geben alle logischen Operatoren wie > == !etc einen intWert von 1oder zurück 0.

Es war für Anwendungen üblich, hausgemachte Typen wie zu verwenden typedef enum { FALSE, TRUE } BOOL;, die sich auch auf intTypen in Größengröße beschränken.

C ++ hatte einen viel besseren und expliziten booleschen Typ bool, der nicht größer als 1 Byte war. Während die booleschen Typen oder Ausdrücke in C im schlimmsten Fall als 4 Bytes enden würden. In C wurde eine gewisse Kompatibilität mit C ++ mit dem C99-Standard eingeführt. C bekam dann einen booleschen Typ _Boolund auch den Header stdbool.h.

stdbool.hbietet einige Kompatibilität mit C ++. Dieser Header definiert das Makro bool(gleiche Schreibweise wie das C ++ - Schlüsselwort), das erweitert _Boolwird. Dieser Typ ist ein kleiner Ganzzahltyp, wahrscheinlich 1 Byte groß. In ähnlicher Weise enthält der Header zwei Makros trueund die falsegleiche Schreibweise wie C ++ - Schlüsselwörter, jedoch mit Abwärtskompatibilität zu älteren C-Programmen . Daher trueund falseerweitern Sie zu 1und 0in C und deren Typ ist int. Diese Makros sind nicht vom booleschen Typ, wie es die entsprechenden C ++ - Schlüsselwörter wären.

In ähnlicher Weise aus Gründen der Abwärtskompatibilität, logische Operatoren in C noch zurückkehren ein intbis heute, obwohl heutzutage C einen Booleschen Typ bekam. In C ++ geben logische Operatoren a zurück bool. Ein Ausdruck wie sizeof(a == b)gibt also die Größe von a intin C an, aber die Größe von a boolin C ++.

In Bezug auf den bedingten Operator ?:

Der bedingte Operator ?:ist ein seltsamer Operator mit ein paar Macken. Es ist ein häufiger Fehler zu glauben, dass es zu 100% äquivalent zu ist if() { } else {}. Nicht ganz.

Es gibt einen Sequenzpunkt zwischen der Auswertung des 1. und des 2. oder 3. Operanden. Es ?:wird garantiert, dass der Operator nur den 2. oder 3. Operanden auswertet, sodass er keine Nebenwirkungen des Operanden ausführen kann, die nicht ausgewertet werden. Code wie true? func1() : func2()wird nicht ausgeführt func2(). So weit, ist es gut.

Es gibt jedoch eine spezielle Regel, die besagt, dass der 2. und 3. Operand implizit vom Typ heraufgestuft und mit den üblichen arithmetischen Konvertierungen gegeneinander abgewogen werden müssen . ( Implizite Typ-Promotion-Regeln in C hier erklärt ). Dies bedeutet, dass der 2. oder 3. Operand immer mindestens so groß wie ein ist int.

Es spielt also keine Rolle trueund ist falsezufällig vom Typ intin C, da der Ausdruck immer mindestens die Größe eines intEgal ergibt .

Selbst wenn Sie den Ausdruck neu schreiben würden, würde er immer noch die Größe eines !sizeof(a ? (bool)true : (bool)false) int

Dies liegt an der impliziten Typwerbung durch die üblichen arithmetischen Konvertierungen.


1
C ++ garantiert nicht wirklich sizeof(bool)==1.
Aschepler

1
@aschepler Nein, aber die reale Welt außerhalb des C ++ - Standards garantiert dies jedoch. Nennen Sie einen Compiler, bei dem es nicht 1 ist.
Lundin

Hallo. Ich denke, diese Antwort wäre ohne den ersten Teil besser . Der zweite Teil beantwortet die Frage. Der Rest ist zwar interessant, aber nur Lärm.
YSC

@YSC Dies war ursprünglich mit C und C ++ gekennzeichnet, daher war ein Vergleich zwischen den verschiedenen Bool-Typen und dem Verlauf dahinter erforderlich. Ich bezweifle, dass ich den ersten Teil geschrieben hätte, wenn nicht das C ++ - Tag gewesen wäre. Man muss jedoch verstehen, warum sizeof (bool) 1 ist, aber sizeof (false) 4 in C.
Lundin

21

Schnelle Antwort:

  • sizeof(a ? true : false)Ergibt 4weil trueund falsedefiniert sind <stdbool.h>als 1und 0jeweils so die Expression Expandiert zu sizeof(a ? 1 : 0)dem mit Typ eine ganze Zahl Ausdruck ist int, die 4 Bytes von der Plattform einnimmt. Aus dem gleichen Grund sizeof(true)würde auch 4auf Ihrem System ausgewertet .

Beachten Sie jedoch Folgendes:

  • sizeof(a ? a : a)wird auch ausgewertet, 4weil der ternäre Operator die ganzzahligen Heraufstufungen für seinen zweiten und dritten Operanden ausführt, wenn es sich um ganzzahlige Ausdrücke handelt. Das gleiche passiert natürlich für sizeof(a ? true : false)und sizeof(a ? (bool)true : (bool)false), aber das Casting des gesamten Ausdrucks boolverhält sich wie erwartet : sizeof((bool)(a ? true : false)) -> 1.

  • Beachten Sie auch, dass Vergleichsoperatoren boolesche Werte auswerten 1oder 0, aber den intTyp haben : sizeof(a == a) -> 4.

Die einzigen Operatoren, die die Boolesche Natur von beibehalten, awären:

  • der Komma-Operator: beides sizeof(a, a)und sizeof(true, a)zur 1Kompilierungszeit auswerten .

  • die Zuweisungsoperatoren: beide sizeof(a = a)und sizeof(a = true)haben einen Wert von 1.

  • die Inkrementoperatoren: sizeof(a++) -> 1

Schließlich gilt alles oben Genannte nur für C: C ++ hat unterschiedliche Semantiken in Bezug auf boolTyp, Boolesche Werte trueund falseVergleichsoperatoren sowie den ternären Operator: Alle diese sizeof()Ausdrücke werden 1in C ++ ausgewertet .


2
Gute Antwort, die es tatsächlich schafft, darauf hinzuweisen, dass es nicht wirklich darauf ankommt, welcher Typ trueund welche falsesind, da die ?:Operanden ohnehin zu einer Ganzzahl intheraufgestuft würden. Somit sizeof(a ? (uint8_t)true : (uint8_t)false)ergibt sich auch 4 als Ergebnis.
Lundin

Diese Antwort deckt den wichtigsten Punkt ab, Wert, der befördert wirdint
Chinni

1

Hier ist ein Ausschnitt, aus dem die Quelle stammt

#ifndef __cplusplus

#define bool    _Bool
#define true    1
#define false   0

#else /* __cplusplus */

Es gibt Makros trueund falsewerden als 1 bzw. 0 deklariert.

In diesem Fall ist der Typ jedoch der Typ der Literalkonstanten. Sowohl 0 als auch 1 sind ganzzahlige Konstanten, die in ein int passen, daher ist ihr Typ int.

und das sizeof(int)in deinem Fall ist 4.


-3

In C gibt es keinen booleschen Datentyp. Stattdessen werden logische Ausdrücke zu ganzzahligen Werten ausgewertet, 1wenn dies nicht der Fall ist 0.

Bedingte Ausdrücke wie if, for, whileoder c ? a : beine ganze Zahl erwarten, wenn die Zahl nicht Null ist zu berücksichtigen ist mit trueAusnahme einiger Sonderfälle, hier ist eine rekursive Summe Funktion , bei der der ternäre-Operator auswerten wird , truebis zu nerreichen 0.

int sum (int n) { return n ? n+sum(n-1) : n ;

Es kann auch verwendet werden, NULLum einen Zeiger zu überprüfen. Hier ist eine rekursive Funktion, die den Inhalt einer einfach verknüpften Liste druckt.

void print(sll * n){ printf("%d -> ",n->val); if(n->next)print(n->next); }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.