Perché "sizeof (a? True: false)" fornisce un output di quattro byte?


133

Ho un piccolo codice sizeofsull'operatore con l'operatore ternario:

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

Uscita ( GCC ):

1
1
4 // Why 4?

Ma qui,

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

l'operatore ternario restituisce il booleantipo e la dimensione del booltipo è 1byte in C.

Allora perché sizeof(a ? true : false)fornisce un output di quattro byte?


39
sizeof(true)ed sizeof(false)è anche 4: ide.geeksforgeeks.org/O5jvuN
tkausl

7
La domanda più interessante qui sarebbe perché questa implementazione è "incoerente" in quanto ovviamente definisce la _Booldimensione 1, ma non truee false. Ma lo standard non ha nulla da dire al riguardo, per quanto posso dire.

12
@FelixPalmen stesso motivo per cui dato char a; sizeof(a) == 1e sizeof('a') == sizeof(int)(in C). Non riguarda l'implementazione, riguarda la lingua.
n. 'pronomi' m.

10
Hai provato a stampare sizeof(true)? forse renderà le cose un po 'più chiare (in particolare, diventerà ovvio che l'operatore ternario è un'aringa rossa).
n. 'pronomi' m.

4
@FelixPalmen trueIS #defined per essere 1 da stdbool.hquindi sì, questa è la definizione letterale.
n. 'pronomi' m.

Risposte:


223

È perché hai #include <stdbool.h>. Quell'intestazione definisce le macro true e falseessere 1e 0, quindi la tua dichiarazione è simile alla seguente:

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

sizeof(int) è 4 sulla tua piattaforma.


21
"È perché hai #include <stdbool.h>" No, non lo è. sizeof(a ? (uint8_t)1 : (uint8_t)0);darebbe anche un risultato di 4. La promozione intera degli ?:operandi è la parte importante qui, non la dimensione di truee false.
Lundin,

9
@Lundin: entrambi sono importanti. Come scritto, il tipo è già intsenza promozione. Il motivo per cui non è possibile "risolvere" sono le promozioni predefinite.
R .. GitHub smette di aiutare ICE il

5
@PeterSchneider Questo non è C ++. Questo è C. In C ++ truee nonfalse sono macro; sono parole chiave. Non sono definiti per essere e , ma per essere i valori veri e falsi del tipo. 10bool
Giustino,

5
@PeterSchneider No, oggi hai imparato qualcosa su C. Non confondere le due lingue. In C ++, sizeof(true)è 1. demo .
Rakete1111,

1
Vero, confuso. Non avevo letto attentamente ed è stato fuorviato da cppreference-link. Colpa mia, grazie. Ma ho comunque questa sensazione riguardo al c ++.
Peter Schneider,

66

Qui, booleantipo di ritorno dell'operatore ternario ,

OK, c'è di più!

In C, il risultato di questa operazione ternaria è di tipo int. [note sotto (1,2)]

Quindi il risultato è lo stesso dell'espressione sizeof(int), sulla tua piattaforma.


Nota 1: Citando C11, capitolo §7.18,Boolean type and values <stdbool.h>

[....] Le restanti tre macro sono adatte per l'uso nelle #ifdirettive di preelaborazione. Loro sono

true

che si espande alla costante intera 1,

false

che si espande alla costante intera 0, [....]

Nota 2: per operatore condizionale, capitolo §6.5.15, ( sottolineatura mia )

Viene valutato il primo operando; esiste un punto di sequenza tra la sua valutazione e la valutazione del secondo o terzo operando (qualunque sia la valutazione). Il secondo operando viene valutato solo se il primo confronta in modo diverso da 0; il terzo operando viene valutato solo se il primo confronta uguale a 0; il risultato è il valore del secondo o terzo operando (qualunque sia valutato), [...]

e

Se sia il secondo che il terzo operando hanno un tipo aritmetico, il tipo di risultato che sarebbe determinato dalle normali conversioni aritmetiche, se fossero applicati a quei due operandi, è il tipo del risultato. [....]

pertanto, il risultato sarà di tipo intero e, a causa dell'intervallo di valori, le costanti sono precisamente di tipo int.

Detto questo, un consiglio generico int main()dovrebbe essere int main (void)quello di essere veramente conforme agli standard.


@ user694733 umm..perché no? <stdbool.h>definisce i MACROS di tipo int.. è sbagliato?
Sourav Ghosh,

@BasileStarynkevitch OK, vedo che ora, questo sembra davvero sbagliato, aggiornato ora.
Sourav Ghosh,

58

L'operatore ternario è un'aringa rossa.

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

stampa 4 (o qualunque cosa sizeof(int)sia sulla tua piattaforma).

Di seguito si presume che boolsia un sinonimo charo un tipo simile di dimensione 1 ed intè maggiore di char.

Il motivo per cui sizeof(true) != sizeof(bool)ed sizeof(true) == sizeof(int)è semplicemente perché nontrue è un'espressione di tipo . È un'espressione di tipo . È come in .boolint#define1stdbool.h

Non ci sono affatto valori di tipo boolin C. Ogni valore viene immediatamente promosso int, anche se usato come argomento per sizeof. Modifica: questo paragrafo non è vero, argomenti per cui sizeofnon essere promossi int. Ciò non influisce su nessuna delle conclusioni.


Bella risposta. Dopo aver letto la risposta attualmente più votata, stavo pensando che tutte le dichiarazioni dovrebbero essere valutate a 4. Ciò ha chiarito le cose. +1
Pedro A

5
Un valore non è (bool)1di tipo bool?
Ben Voigt,

printf("%u\n", sizeof((char) 1));stampe 1sulla mia piattaforma mentre printf("%u\n", sizeof(1));stampe 4. Questo non significa che la tua affermazione "Ogni valore di questo tipo viene immediatamente promosso a int, anche se usato come argomento per sizeof" è falso?
JonatanE

Questo non risponde davvero alla domanda. Le dimensioni e il tipo di trueetc non contano davvero nel caso in ?:cui vengono intcomunque promossi numeri interi . Cioè, la risposta dovrebbe affrontare perché ?: è un'aringa rossa.
Lundin,

6
Penso che la risposta affronti il ​​problema nel miglior modo possibile. È possibile effettuare il downgrade o migliorarlo.
n. 'pronomi' m.

31

Per quanto riguarda il tipo booleano in C

Un tipo booleano è stato introdotto abbastanza tardi nel linguaggio C, nell'anno 1999. Prima di allora, C non aveva un tipo booleano ma era invece usato intper tutte le espressioni booleane. Pertanto tutti gli operatori logici come > == !etc restituiscono un intvalore 1o 0.

Era personalizzato per le applicazioni utilizzare tipi fatti in casa come typedef enum { FALSE, TRUE } BOOL;, che si riduce anche a inttipi di dimensioni.

Il C ++ aveva un tipo booleano molto migliore ed esplicito bool, che non era più grande di 1 byte. Mentre i tipi o le espressioni booleani in C finirebbero per 4 byte nel peggiore dei casi. Un certo modo di compatibilità con C ++ è stato introdotto in C con lo standard C99. C ha quindi ottenuto un tipo booleano _Boole anche l'intestazione stdbool.h.

stdbool.hfornisce una certa compatibilità con C ++. Questa intestazione definisce la macro bool(stessa ortografia della parola chiave C ++) che si espande _Bool, un tipo che è un tipo intero piccolo, probabilmente 1 byte grande. Allo stesso modo, l'intestazione fornisce due macro truee la falsestessa ortografia delle parole chiave C ++, ma con retrocompatibilità con i programmi C precedenti . Pertanto trueed falseespandersi in 1e 0in C e il loro tipo è int. Queste macro non sono in realtà del tipo booleano come sarebbero le parole chiave C ++ corrispondenti.

Allo stesso modo, ai fini della retrocompatibilità, gli operatori logici in C tornano ancoraint oggi, anche se al giorno C ha ottenuto un tipo booleano. In C ++, gli operatori logici restituiscono a bool. Quindi un'espressione come sizeof(a == b)darà la dimensione di a intin C, ma la dimensione di a boolin C ++.

Per quanto riguarda l'operatore condizionale ?:

L'operatore condizionale ?:è un operatore strano con un paio di stranezze. È un errore comune credere che sia equivalente al 100% if() { } else {}. Non proprio.

Esiste un punto di sequenza tra la valutazione del 1o e 2o o 3o operando. L' ?:operatore ha la garanzia di valutare solo il 2o o il 3o operando, quindi non può eseguire alcun effetto collaterale dell'operando che non viene valutato. Il codice come true? func1() : func2()non verrà eseguito func2(). Fin qui tutto bene.

Tuttavia , esiste una regola speciale che afferma che il 2 ° e il 3 ° operando devono essere promossi in modo implicito e bilanciati l'uno contro l'altro con le solite conversioni aritmetiche . ( Regole implicite di promozione del tipo in C spiegate qui ). Ciò significa che il 2o o 3o operando sarà sempre almeno grande quanto un int.

Quindi non importa truee falsecapita di essere di tipo intin C perché l'espressione darebbe sempre almeno la dimensione di un intnon importa.

Anche se riscrivi l'espressione in esso, restituiresti comunque la dimensione di un !sizeof(a ? (bool)true : (bool)false) int

Ciò è dovuto alla promozione del tipo implicito attraverso le solite conversioni aritmetiche.


1
Il C ++ in realtà non garantisce sizeof(bool)==1.
aschepler

1
@aschepler No, ma il mondo reale al di fuori dello standard C ++ lo garantisce comunque. Nomina un compilatore dove non è 1.
Lundin

Ciao. Penso che questa risposta sarebbe migliore senza la sua prima parte. La seconda parte risponde alla domanda. Il resto, sebbene interessante, è solo rumore.
YSC,

@YSC Questo era originariamente etichettato come C e C ++, quindi era necessario un confronto tra i loro diversi tipi di bool e la storia dietro di loro. Dubito che avrei scritto la prima parte se non fosse stato per il tag C ++. Tuttavia, bisogna capire perché sizeof (bool) è 1 ma sizeof (false) è 4 in C.
Lundin,

21

Risposta rapida:

  • sizeof(a ? true : false)valuta 4perché truee falsesono definiti rispettivamente <stdbool.h>come 1e 0, quindi l'espressione si espande a sizeof(a ? 1 : 0)cui è un'espressione intera con tipo int, che occupa 4 byte sulla piattaforma. Per lo stesso motivo, sizeof(true)valuteresti anche 4sul tuo sistema.

Si noti tuttavia che:

  • sizeof(a ? a : a)valuta anche 4perché l'operatore ternario esegue le promozioni intere sul suo secondo e terzo operando se si tratta di espressioni intere. La stessa cosa accade per sizeof(a ? true : false)e sizeof(a ? (bool)true : (bool)false), ma colata l'intera espressione come boolsi comporta come previsto: sizeof((bool)(a ? true : false)) -> 1.

  • Si noti inoltre che gli operatori di confronto restituire valori booleani 1o 0, ma hanno inttipo: sizeof(a == a) -> 4.

Gli unici operatori che mantengono la natura booleana di asarebbero:

  • l'operatore virgola: entrambi sizeof(a, a)e sizeof(true, a)valutali 1in fase di compilazione.

  • gli operatori di assegnazione: entrambi sizeof(a = a)e sizeof(a = true)hanno un valore di 1.

  • gli operatori di incremento: sizeof(a++) -> 1

Infine, tutto quanto sopra si applica solo a C: C ++ ha una semantica diversa per quanto riguarda il booltipo, i valori booleani truee gli falseoperatori di confronto e l'operatore ternario: tutte queste sizeof()espressioni vengono valutate 1in C ++.


2
Una buona risposta che in realtà riesce a sottolineare che non importa davvero quale tipo truee che cosa falsesono, perché gli ?:operandi verrebbero intcomunque promossi numeri interi . In questo modo si sizeof(a ? (uint8_t)true : (uint8_t)false)otterrà anche 4 come risultato.
Lundin,

Questa risposta copre il principale punto importante, il valore che viene promosso aint
Chinni il

1

Ecco uno snippet da cui è incluso ciò che è nella fonte

#ifndef __cplusplus

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

#else /* __cplusplus */

Ci macro truee falsesono dichiarati rispettivamente come 1 e 0.

tuttavia in questo caso il tipo è il tipo delle costanti letterali. Sia 0 che 1 sono costanti intere che rientrano in un int, quindi il loro tipo è int.

e sizeof(int)nel tuo caso è 4.


-3

Non esiste un tipo di dati booleano in C, invece le espressioni logiche valutano valori interi 1se vero altrimenti 0.

Espressioni condizionali piace if, for,while , o c ? a : bsi aspettano un numero intero, se il numero è diverso da zero è considerato truead eccezione di alcuni casi particolari, ecco una funzione di somma ricorsiva in cui il ternario operatore valuterà truefino nportata 0.

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

Può anche essere usato per NULLcontrollare un puntatore, ecco una funzione ricorsiva che stampa il contenuto di un Singly-Linked-List.

void print(sll * n){ printf("%d -> ",n->val); if(n->next)print(n->next); }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.