Dlaczego „sizeof (a? True: false)” daje wynik o wielkości czterech bajtów?

133

Mam mały fragment kodu o sizeofoperatorze z operatorem trójskładnikowym:

#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;
}

Wyjście ( GCC ):

1
1
4 // Why 4?

Ale tu,

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

operator trójargumentowy zwraca booleantyp, a rozmiar booltypu to 1bajt w C.

Wtedy dlaczego sizeof(a ? true : false)dać moc czterech bajtów?

msc
źródło
39
sizeof(true)a sizeof(false)także 4: ide.geeksforgeeks.org/O5jvuN
tkausl
7
Bardziej interesującym pytaniem byłoby to, dlaczego ta implementacja jest „niespójna”, ponieważ oczywiście definiuje _Boolrozmiar 1, ale nie truei false. Ale standard nie ma nic do powiedzenia na ten temat, o ile wiem.
12
@FelixPalmen ten sam powód, dla którego podano char a; sizeof(a) == 1i sizeof('a') == sizeof(int)(w C). Nie chodzi o implementację, chodzi o język.
n. zaimki m.
10
Czy próbowałeś drukować sizeof(true)? być może sprawi, że rozrzedzenie będzie trochę bardziej wyraźne (w szczególności stanie się oczywiste, że operatorem trójskładnikowym jest czerwony śledź).
n. zaimki m.
4
@FelixPalmen truejest równe #define1, stdbool.hwięc tak, to jest dosłowna definicja.
n. zaimki m.

Odpowiedzi:

223

To dlatego, że masz #include <stdbool.h>. Ten nagłówek definiuje makra true i falsebyć 1i 0, więc twoje oświadczenie wygląda następująco:

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

sizeof(int) to 4 na twojej platformie.

Justin
źródło
21
„To dlatego, że masz #include <stdbool.h>” Nie, nie jest. sizeof(a ? (uint8_t)1 : (uint8_t)0);dałoby również wynik 4. Promocja liczb całkowitych ?:operandów jest tutaj ważną częścią, a nie rozmiar truei false.
Lundin
9
@Lundin: Oba są ważne. Jak napisano, typ jest już intbez promocji. Powodem, dla którego nie można tego „naprawić”, są domyślne promocje.
R .. GitHub STOP HELPING ICE
5
@PeterSchneider To nie jest C ++. To jest C. W C ++ truei niefalse są makrami; są to słowa kluczowe. Nie są zdefiniowane jako i , ale jako prawdziwe i fałszywe wartości typu. 10bool
Justin
5
@PeterSchneider Nie, dowiedziałeś się dziś czegoś o C. Nie myl tych dwóch języków. W C ++ sizeof(true)to 1. demo .
Rakete1111
1
To prawda, pomieszałem. Nie przeczytał uważnie i został zmylony przez link cppreference-link. Moja wina, dziękuję. Ale i tak mam takie przeczucie co do C ++.
Peter Schneider
66

Tutaj, booleantyp zwracany przez operatora trójskładnikowego ,

OK, to nie wszystko!

W C wynik tej trójskładnikowej operacji jest typu int. [uwagi poniżej (1, 2)]

Stąd wynik jest taki sam jak wyrażenie sizeof(int)na Twojej platformie.


Uwaga 1: Cytowanie C11, rozdział §7.18,Boolean type and values <stdbool.h>

[…] Pozostałe trzy makra nadają się do użycia w #ifdyrektywach przetwarzania wstępnego. Oni są

true

która rozwija się do stałej liczby całkowitej 1,

false

która jest interpretowana jako stała całkowita 0, [....]

Uwaga 2: Dla operatora warunkowego, rozdział §6.5.15, ( moje wyróżnienie )

Obliczany jest pierwszy operand; istnieje punkt sekwencji między jego oceną a oceną drugiego lub trzeciego operandu (w zależności od tego, który z nich jest oceniany). Drugi operand jest oceniany tylko wtedy, gdy pierwszy porównuje nierówność z 0; trzeci operand jest oceniany tylko wtedy, gdy pierwszy porównuje równy 0;wynik jest wartością drugiego lub trzeciego operandu (w zależności od tego, który z nich jest oceniany), [...]

i

Jeśli zarówno drugi, jak i trzeci operand mają typ arytmetyczny, typ wyniku, który zostałby określony przez zwykłe konwersje arytmetyczne, gdyby zostały zastosowane do tych dwóch operandów, jest typem wyniku.[…]

stąd wynik będzie typu integer, a ze względu na zakres wartości stałe są dokładnie typu int .

To powiedziawszy, ogólna rada int main()powinna być lepsza, int main (void)aby być naprawdę zgodnym ze standardami.

Sourav Ghosh
źródło
@ user694733 umm..Dlaczego nie? <stdbool.h>definiuje MAKRA jako typu int.. czy to źle?
Sourav Ghosh
@BasileStarynkevitch OK, teraz widzę, to rzeczywiście wydaje się błędne, teraz zaktualizowane.
Sourav Ghosh
58

Operator trójskładnikowy to śledź czerwony.

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

drukuje 4 (lub cokolwiek sizeof(int)jest na twojej platformie).

Poniższy tekst zakłada, że booljest to synonim charrozmiaru 1 lub podobnego typu i intjest większy niż char.

Powodem, sizeof(true) != sizeof(bool)i sizeof(true) == sizeof(int)to tylko dlatego, że trueto nie jest wyrazem typu bool. To wyraz typu int. Jest #definejak 1w stdbool.h.

W ogóle nie ma wartości typu r bool. Każda taka wartość r jest natychmiast promowana do int, nawet jeśli jest używana jako argument do sizeof. Edycja: ten akapit nie jest prawdziwy, argumenty, do sizeofktórych nie można awansować int. Nie wpływa to jednak na żadne z wniosków.

n. zaimki m.
źródło
Niezła odpowiedź. Po przeczytaniu odpowiedzi, która jest obecnie najbardziej przychylna, pomyślałem, że wszystkie stwierdzenia powinny dać 4. To wyjaśniło sprawę. +1
Pedro A
5
Czy nie (bool)1jest wartością typu r bool?
Ben Voigt
printf("%u\n", sizeof((char) 1));drukuje 1na mojej platformie, podczas gdy printf("%u\n", sizeof(1));drukuje 4. Czy to nie znaczy, że twoje stwierdzenie „Każda taka wartość r jest natychmiast promowana do int, nawet gdy jest używana jako argument do sizeof” jest fałszywa?
JonatanE
To naprawdę nie odpowiada na pytanie. Rozmiar i typ trueetc nie ma tak naprawdę znaczenia w przypadku, ?:ponieważ i tak jest promowany jako liczba całkowita int. Oznacza to, że odpowiedź powinna wskazywać, dlaczego ?: jest czerwony śledź.
Lundin
6
Myślę, że odpowiedź rozwiązuje ten problem w najlepszy możliwy sposób. Możesz go przeciwdziałać lub ulepszać.
n. zaimki m.
31

Odnośnie typu boolowskiego w C

Typ boolowski został wprowadzony dość późno w języku C, w roku 1999. Wcześniej C nie posiadał typu boolowskiego, ale zamiast tego był używany intwe wszystkich wyrażeniach boolowskich. Dlatego wszystkie operatory logiczne, takie jak > == !etc zwracają intwartość 1lub 0.

Niestandardowe było, aby aplikacje korzystały z typów domowych, takich jak typedef enum { FALSE, TRUE } BOOL;, co również sprowadza się do inttypów o dużych rozmiarach.

C ++ miał znacznie lepszy i jawny typ boolowski bool, który nie był większy niż 1 bajt. Podczas gdy typy boolowskie lub wyrażenia w C w najgorszym przypadku miałyby 4 bajty. Pewien sposób na kompatybilność z C ++ został wprowadzony w C ze standardem C99. C otrzymał wtedy typ boolowski, _Boola także nagłówek stdbool.h.

stdbool.hzapewnia pewną zgodność z C ++. Ten nagłówek definiuje makro bool(taka sama pisownia jak słowo kluczowe w C ++), które rozwija się do _Bool, typ, który jest typem małej liczby całkowitej, prawdopodobnie o rozmiarze 1 bajtu. Podobnie, nagłówek zawiera dwa makra truei false, tak samo jak C ++ pisowni słów kluczowych, ale z wstecznej kompatybilności dla starszych programów C . Dlatego truei falserozwinąć do 1iw 0C, a ich typ to int. Te makra nie są w rzeczywistości typu boolowskiego, jak odpowiadałyby im słowa kluczowe C ++.

Podobnie, dla celów zgodności z poprzednimi wersjami, operatory logiczne w C nadal zwracają an intdo dnia dzisiejszego, mimo że C obecnie ma typ boolowski. W C ++ operatory logiczne zwracają plik bool. Zatem wyrażenie takie jak sizeof(a == b)da rozmiar litery intw C, ale rozmiar a boolw C ++.

Odnośnie operatora warunkowego ?:

Operator warunkowy ?:jest dziwnym operatorem z kilkoma dziwactwami. Powszechnym błędem jest przekonanie, że jest to równoważne w 100% if() { } else {}. Nie do końca.

Istnieje punkt sekwencji między obliczeniem pierwszego i drugiego lub trzeciego operandu. ?:Operator gwarantuje jedynie oceniać albo 2 lub 3rd argumentu, więc nie może wykonywać żadnych skutków ubocznych argumentu, który nie jest uwzględniany. Kod taki jak true? func1() : func2()nie będzie wykonywany func2(). Na razie w porządku.

Istnieje jednak specjalna reguła mówiąca, że ​​drugi i trzeci operand muszą być niejawnie promowane i równoważone względem siebie za pomocą zwykłych konwersji arytmetycznych . ( Wyjaśniono tutaj niejawne reguły promocji typu w języku C ). Oznacza to, że drugi lub trzeci operand zawsze będzie co najmniej tak duży jak plik int.

Więc to nie ma znaczenia truei falsetak się składa, że jest to typ intw C, ponieważ wyrażenie zawsze podawałoby co najmniej rozmiar intnieważne.

Nawet gdybyś przepisał wyrażenie na to, nadal zwróciłoby to rozmiar !sizeof(a ? (bool)true : (bool)false) int

Dzieje się tak z powodu niejawnej promocji typu poprzez zwykłe konwersje arytmetyczne.

Lundin
źródło
1
C ++ w rzeczywistości nie gwarantuje sizeof(bool)==1.
aschepler
1
@aschepler Nie, ale realny świat poza standardem C ++ gwarantuje to. Wymień jeden kompilator, gdzie nie jest 1.
Lundin
Cześć. Myślę, że ta odpowiedź byłaby lepsza bez jej pierwszej części. Druga część odpowiada na to pytanie. Reszta, choć interesująca, to po prostu hałas.
YSC
@YSC To było pierwotnie oznaczone jako C i C ++, więc konieczne było porównanie między ich różnymi typami bool i historią za nimi. Wątpię, bym napisał pierwszą część, gdyby nie tag C ++. Jednak trzeba zrozumieć, dlaczego sizeof (bool) wynosi 1, ale sizeof (false) to 4 w C.
Lundin,
21

Szybka odpowiedź:

  • sizeof(a ? true : false)szacuje się na 4ponieważ truei falsesą zdefiniowane odpowiednio w <stdbool.h>as 1i 0, więc wyrażenie rozwija się do wyrażenia sizeof(a ? 1 : 0)będącego liczbą całkowitą o typie int, zajmującego 4 bajty na Twojej platformie. Z tego samego powodu sizeof(true)powinien również ocenić 4w twoim systemie.

Należy jednak pamiętać, że:

  • sizeof(a ? a : a)4oblicza się również do, ponieważ operator trójskładnikowy wykonuje promocje liczb całkowitych na swoim drugim i trzecim operandzie, jeśli są to wyrażenia całkowite. To samo dzieje się w trakcie sizeof(a ? true : false), a sizeof(a ? (bool)true : (bool)false), a odlanie całego ekspresji jak boolzachowuje się, jak oczekiwano sizeof((bool)(a ? true : false)) -> 1.

  • również pamiętać, że operatory porównania oceny do wartości logicznych 1lub 0, ale mają inttyp: sizeof(a == a) -> 4.

Jedynymi operatorami, które zachowują logiczną naturę, abyłyby:

  • operator przecinka: oba sizeof(a, a)i sizeof(true, a)obliczane do 1w czasie kompilacji.

  • operatory przypisania: both sizeof(a = a)i sizeof(a = true)mają wartość 1.

  • operatory przyrostu: sizeof(a++) -> 1

Wreszcie, wszystkie powyższe dotyczą tylko C: C ++ ma inną semantykę dotyczącą booltypu, wartości logicznych truei falseoperatorów porównania oraz operatora trójskładnikowego: wszystkie te sizeof()wyrażenia są obliczane 1w C ++.

chqrlie
źródło
2
Dobra odpowiedź, która faktycznie udaje się wskazać, że tak naprawdę nie ma znaczenia, jakiego typu truei falsesą, ponieważ ?:operandy i tak byłyby promowane do liczby całkowitej int. W sizeof(a ? (uint8_t)true : (uint8_t)false)rezultacie również da wynik 4.
Lundin
Ta odpowiedź obejmuje główny ważny punkt, wartość awansuint
Chinni
1

Oto fragment, z którego pochodzi zawartość

#ifndef __cplusplus

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

#else /* __cplusplus */

Tam makra truei falsesą zadeklarowane odpowiednio jako 1 i 0.

jednak w tym przypadku typ jest typem stałych literału. Zarówno 0, jak i 1 to stałe całkowite, które mieszczą się w int, więc ich typ to int.

aw sizeof(int)twoim przypadku to 4.

u__
źródło
-3

W języku C nie ma logicznego typu danych, zamiast tego wyrażenia logiczne są obliczane na wartości całkowite, 1jeśli prawda, w przeciwnym razie0 .

Wyrażenia warunkowe podoba if, for, while, lub c ? a : boczekiwać liczbę całkowitą, jeśli liczba jest różna od zera to uważane trueza wyjątkiem pewnych szczególnych przypadkach, oto funkcji rekurencyjnej suma w którym trójargumentowy-operator oceni truenzasięgiem 0.

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

Może być również użyty do NULLsprawdzenia wskaźnika, oto funkcja rekurencyjna, która wypisuje zawartość listy połączonej pojedynczo.

void print(sll * n){ printf("%d -> ",n->val); if(n->next)print(n->next); }
Khaled.K
źródło