Pourquoi «sizeof (a? True: false)» donne-t-il une sortie de quatre octets?

133

J'ai un petit morceau de code sur l' sizeofopérateur avec l'opérateur ternaire:

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

Sortie ( GCC ):

1
1
4 // Why 4?

Mais ici,

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

l'opérateur ternaire renvoie le booleantype et la taille du booltype est l' 1octet en C.

Alors pourquoi sizeof(a ? true : false)donne-t-il une sortie de quatre octets?

msc
la source
39
sizeof(true)et sizeof(false)c'est aussi 4: ide.geeksforgeeks.org/O5jvuN
tkausl
7
La question la plus intéressante ici serait de savoir pourquoi cette implémentation est "incohérente" en ce qu'elle définit évidemment la _Booltaille 1, mais pas trueet false. Mais la norme n'a rien à dire à ce sujet pour autant que je sache.
12
@FelixPalmen même raison pour laquelle donné char a; sizeof(a) == 1et sizeof('a') == sizeof(int)(en C). Il ne s'agit pas de la mise en œuvre, mais de la langue.
n. «pronoms» m.
10
Avez-vous essayé d'imprimer sizeof(true)? peut-être que cela rendra les minces un peu plus claires (en particulier, il deviendra évident que l'opérateur ternaire est un hareng rouge).
n. «pronoms» m.
4
@FelixPalmen trueest #defined être égal à 1, stdbool.hdonc oui, c'est la définition littérale.
n. «pronoms» m.

Réponses:

223

C'est parce que vous avez #include <stdbool.h>. Cet en-tête définit les macros true et falseêtre 1et 0, donc votre déclaration ressemble à ceci:

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

sizeof(int) est 4 sur votre plateforme.

Justin
la source
21
"C'est parce que vous avez #include <stdbool.h>" Non, ce n'est pas le cas. sizeof(a ? (uint8_t)1 : (uint8_t)0);donnerait également un résultat de 4. La promotion entière des ?:opérandes est la partie importante ici, pas la taille de trueet false.
Lundin
9
@Lundin: Les deux sont importants. Tel qu'il est écrit, le type est déjà intsans promotion. La raison pour laquelle vous ne pouvez pas "réparer" ce sont les promotions par défaut.
R .. GitHub STOP AIDER ICE
5
@PeterSchneider Ce n'est pas du C ++. Ceci est C. En C ++, trueet nefalse sont pas des macros; ce sont des mots clés. Ils ne sont pas définis pour être 1et 0, mais pour être les valeurs vraie et fausse du booltype.
Justin
5
@PeterSchneider Non, vous avez appris quelque chose sur C aujourd'hui. Ne confondez pas les deux langues. En C ++, sizeof(true)est 1. démo .
Rakete1111
1
C'est vrai, mélangé. Je n'avais pas lu attentivement et a été mal lu par cppreference-link. Ma faute, merci. Mais j'ai quand même ce sentiment à propos du C ++.
Peter Schneider
66

Ici, le booleantype de retour d'opérateur ternaire ,

OK, il y a plus à ça!

En C, le résultat de cette opération ternaire est de type int. [notes ci-dessous (1, 2)]

Par conséquent, le résultat est le même que l'expression sizeof(int), sur votre plateforme.


Note 1: C11Citations, chapitre §7.18,Boolean type and values <stdbool.h>

[....] Les trois macros restantes conviennent pour une utilisation dans les #ifdirectives de prétraitement. Elles sont

true

qui se développe jusqu'à la constante entière 1,

false

qui se développe jusqu'à la constante entière 0, [....]

Note 2: Pour l'opérateur conditionnel, chapitre §6.5.15, (c'est moi qui souligne )

Le premier opérande est évalué; il y a un point de séquence entre son évaluation et l'évaluation du deuxième ou du troisième opérande (selon celui qui est évalué). Le deuxième opérande n'est évalué que si le premier est différent de 0; le troisième opérande n'est évalué que si le premier compare égal à 0; le résultat est la valeur du deuxième ou du troisième opérande (selon l'évaluation), [...]

et

Si les deuxième et troisième opérandes ont un type arithmétique, le type de résultat qui serait déterminé par les conversions arithmétiques habituelles, si elles étaient appliquées à ces deux opérandes, est le type du résultat. [....]

par conséquent, le résultat sera de type entier et en raison de la plage de valeurs, les constantes sont précisément de type int.

Cela dit, un conseil générique int main()devrait être int main (void)d'être vraiment conforme aux normes.

Sourav Ghosh
la source
@ user694733 umm .. pourquoi pas? <stdbool.h>définit les MACROS comme étant de type int... est-ce faux?
Sourav Ghosh
@BasileStarynkevitch OK, je vois que maintenant, cela semble en effet faux, mis à jour maintenant.
Sourav Ghosh
58

L'opérateur ternaire est un hareng rouge.

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

imprime 4 (ou ce qui se sizeof(int)trouve sur votre plate-forme).

Ce qui suit suppose qu'il bools'agit d'un synonyme de charou d'un type similaire de taille 1 et qu'il intest supérieur à char.

La raison pour laquelle sizeof(true) != sizeof(bool)et sizeof(true) == sizeof(int)est simplement parce que truen'est pas une expression de type bool. C'est une expression de type int. C'est #defined comme 1dans stdbool.h.

Il n'y a pas du tout de valeurs de type boolen C. Chaque rvalue de ce type est immédiatement promue vers int, même lorsqu'elle est utilisée comme argument de sizeof. Edit: ce paragraphe n'est pas vrai, arguments pour sizeofne pas être promuint . Cela n'affecte cependant aucune des conclusions.

n. «pronoms» m.
la source
Bonne réponse. Après avoir lu la réponse actuellement la plus votée, je pensais que toutes les déclarations devraient être évaluées à 4. Cela a clarifié les choses. +1
Pedro A
5
Une (bool)1rvalue n'est-elle pas de type bool?
Ben Voigt
printf("%u\n", sizeof((char) 1));imprime 1sur ma plate-forme tandis que printf("%u\n", sizeof(1));imprime 4. Cela ne signifie-t-il pas que votre déclaration "Chaque rvalue est immédiatement promue en int, même lorsqu'elle est utilisée comme argument de sizeof" est fausse?
JonatanE
Cela ne répond pas vraiment à la question. La taille et le type de trueetc n'a pas vraiment d'importance dans le cas de ?:car il obtient de inttoute façon un entier promu . Autrement dit, la réponse devrait expliquer pourquoi ?: est un hareng rouge.
Lundin
6
Je pense que la réponse aborde la question de la meilleure façon possible. Vous êtes invités à voter contre ou à l'améliorer.
n. «pronoms» m.
31

Concernant le type booléen en C

Un type booléen a été introduit assez tardivement dans le langage C, en 1999. Auparavant, C n'avait pas de type booléen mais était plutôt utilisé intpour toutes les expressions booléennes. Par conséquent, tous les opérateurs logiques tels que > == !etc renvoient une intvaleur 1ou 0.

Il était habituel pour les applications d'utiliser des types faits maison tels que typedef enum { FALSE, TRUE } BOOL;, qui se résume également à des inttypes de taille réduite.

C ++ avait un type booléen bien meilleur et explicite bool, qui ne dépassait pas 1 octet. Alors que les types ou expressions booléens en C finiraient par 4 octets dans le pire des cas. Une certaine compatibilité avec C ++ a été introduite en C avec la norme C99. C a alors obtenu un type booléen _Boolet également l'en-tête stdbool.h.

stdbool.hfournit une certaine compatibilité avec C ++. Cet en-tête définit la macro bool(même orthographe que le mot clé C ++) qui se développe en _Bool, un type qui est un petit type entier, probablement 1 octet de large. De même, l'en-tête fournit deux macros trueet la falsemême orthographe que les mots-clés C ++, mais avec une compatibilité descendante avec les programmes C plus anciens . Par conséquent, trueet falsedéveloppez vers 1et 0dans C et leur type est int. Ces macros ne sont pas réellement de type booléen comme le seraient les mots-clés C ++ correspondants.

De même, à des fins de compatibilité ascendante, les opérateurs logiques en C renvoient toujours un intà ce jour, même si C de nos jours a un type booléen. En C ++, les opérateurs logiques renvoient un bool. Ainsi, une expression telle que sizeof(a == b)donnera la taille d'un inten C, mais la taille d'un boolen C ++.

Concernant l'opérateur conditionnel ?:

L'opérateur conditionnel ?:est un opérateur étrange avec quelques bizarreries. C'est une erreur courante de croire que cela équivaut à 100% if() { } else {}. Pas assez.

Il y a un point de séquence entre l'évaluation du 1er et du 2ème ou 3ème opérande. Il ?:est garanti que l'opérateur n'évalue que le 2ème ou le 3ème opérande, il ne peut donc exécuter aucun effet secondaire de l'opérande qui n'est pas évalué. Le code comme true? func1() : func2()ne s'exécutera pas func2(). Jusqu'ici tout va bien.

Cependant , il existe une règle spéciale indiquant que les 2ème et 3ème opérandes doivent être implicitement promus et équilibrés les uns par rapport aux autres avec les conversions arithmétiques habituelles . ( Les règles de promotion de type implicite en C expliquées ici ). Cela signifie que le 2ème ou 3ème opérande sera toujours au moins aussi grand qu'un int.

Donc cela n'a pas d'importance trueet falseil se trouve qu'il est de type inten C parce que l'expression donnerait toujours au moins la taille d'un intpeu importe.

Même si vous réécriviez l'expression, elle renverrait toujours la taille d'un !sizeof(a ? (bool)true : (bool)false) int

Cela est dû à la promotion de type implicite via les conversions arithmétiques habituelles.

Lundin
la source
1
C ++ ne garantit pas réellement sizeof(bool)==1.
aschepler
1
@aschepler Non mais le monde réel en dehors du standard C ++ le garantit cependant. Nommez un compilateur là où ce n'est pas 1.
Lundin
Salut. Je pense que cette réponse serait meilleure sans sa première partie. La deuxième partie répond à la question. Le reste, bien qu'intéressant, n'est que du bruit.
YSC
@YSC Cela était à l'origine étiqueté C et C ++, donc une comparaison entre leurs différents types de booléens et l'historique derrière eux était nécessaire. Je doute que j'aurais écrit la première partie sans la balise C ++. Cependant, il faut comprendre pourquoi sizeof (bool) est 1 mais sizeof (false) est 4 en C.
Lundin
21

Réponse rapide:

  • sizeof(a ? true : false)s'évalue à 4parce que trueet falsesont définis respectivement dans <stdbool.h>as 1et 0, donc l'expression se développe ensizeof(a ? 1 : 0) une expression entière de type int, qui occupe 4 octets sur votre plate-forme. Pour la même raison, sizeof(true)serait également évalué 4sur votre système.

Notez cependant que:

  • sizeof(a ? a : a)évalue également à 4parce que l'opérateur ternaire effectue les promotions d'entiers sur ses deuxième et troisième opérandes s'il s'agit d'expressions d'entiers. La même chose se produit bien sûr pour sizeof(a ? true : false)etsizeof(a ? (bool)true : (bool)false) , mais jetant toute expression boolse comporte comme prévu: sizeof((bool)(a ? true : false)) -> 1.

  • noter également que les opérateurs de comparaison évaluer les valeurs booléennes 1ou 0, mais ont le inttype: sizeof(a == a) -> 4.

Les seuls opérateurs qui conservent la nature booléenne de aseraient:

  • l'opérateur virgule: les deux sizeof(a, a)et sizeof(true, a)évaluer à 1au moment de la compilation.

  • les opérateurs d'affectation: les deux sizeof(a = a)et sizeof(a = true)ont une valeur de 1.

  • les opérateurs d'incrémentation: sizeof(a++) -> 1

Enfin, tout ce qui précède s'applique uniquement à C: C ++ a une sémantique différente concernant le booltype, les valeurs booléennes trueet false, les opérateurs de comparaison et l'opérateur ternaire: toutes ces sizeof()expressions sont évaluées 1en C ++.

chqrlie
la source
2
Bonne réponse qui parvient en fait à souligner que le type trueet le type importent peu false, car les ?:opérandes obtiendraient de inttoute façon un entier promu . Ainsi sizeof(a ? (uint8_t)true : (uint8_t)false)donnera également 4 comme résultat.
Lundin
Cette réponse couvre le principal point important, la valeur d'être promu àint
Chinni
1

Voici un extrait de ce qui est inclus dans la source

#ifndef __cplusplus

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

#else /* __cplusplus */

Il y a des macros trueetfalse sont déclarés comme 1 et 0 respectivement.

cependant, dans ce cas, le type est le type des constantes littérales. Les deux 0 et 1 sont des constantes entières qui tiennent dans un int, leur type est donc int.

et le sizeof(int)dans votre cas est 4.

u__
la source
-3

Il n'y a pas de type de données booléen en C, à la place les expressions logiques s'évaluent à des valeurs entières 1quand elles sont vraies sinon 0.

Les expressions conditionnelles aiment if, for, whileou c ? a : battendre un entier, si le nombre est qu'il est non nul considéré , truesauf pour certains cas particuliers, voici une fonction somme récurrente dans laquelle l'opérateur ternaire évaluera truejusqu'à nportée 0.

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

Il peut également être utilisé pour NULLvérifier un pointeur, voici une fonction récursive qui imprime le contenu d'une liste à lien unique.

void print(sll * n){ printf("%d -> ",n->val); if(n->next)print(n->next); }
Khaled.K
la source