J'ai un petit morceau de code sur l'opérateur sizeof
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 retourne boolean
type et sizeof bool
le type est 1
octet en C.
Alors pourquoi sizeof(a ? true : false)
donne-t-il une sortie de quatre octets?
C'est parce que vous avez #include <stdbool.h>
. Cet en-tête définit les macrostrue
et false
doit être 1
et 0
, ainsi votre déclaration se présente comme suit:
printf("%zu\n", sizeof(a ? 1 : 0)); // Why 4?
sizeof(int)
est égal à 4 sur votre plate-forme.
Ici, l'opérateur ternaire renvoie
boolean
type,
OK, il y a plus que ça!
En C, le résultat de l'opération this est de type int
. [notes ci-dessous (1,2)]
Le résultat est donc le même que l'expression sizeof(int)
, sur votre plate-forme.
Note 1: Citation de C11
, chapitre §7.18, Boolean type and values <stdbool.h>
[....] Les trois macros restantes peuvent être utilisées dans les directives de pré-traitement
#if
. Elles sont
true
qui se développe à la constante entière 1,
false
qui se développe à la constante entière 0, [....]
Note 2: Pour l'opérateur conditionnel, chapitre §6.5.15, ( l'emphase mine )
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 l'évaluation). Le deuxième opérande est évalué uniquement si le premier est différent de 0; le troisième opérande est évalué uniquement 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 tous deux un type arithmétique, le type de résultat qui serait déterminé par les conversions arithmétiques habituelles, s’ils étaient appliqués à 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 mieux être int main (void)
pour être vraiment conforme aux normes.
L'opérateur ternaire est un hareng rouge.
printf("%zu\n", sizeof(true));
affiche 4 (ou tout ce que sizeof(int)
se trouve sur votre plate-forme).
Ce qui suit suppose que bool
est un synonyme de char
ou d'un type similaire de taille 1 et que int
est supérieur à char
.
La raison pour laquelle sizeof(true) != sizeof(bool)
et sizeof(true) == sizeof(int)
est simplement parce que true
est not une expression de type bool
. C'est une expression de type int
. C'est #define
d comme 1
dans stdbool.h
.
Il n'y a pas du tout de rvalues de type Edit: ce paragraphe n’est pas vrai, les arguments de bool
en C. Chacune de ces valeurs est immédiatement promue en int
, même si elle est utilisée comme argument pour sizeof
.sizeof
ne sont pas promus en int
. Cela n'affecte cependant aucune des conclusions.
Concernant le type booléen en C
Un type booléen a été introduit assez tard dans le langage C, en 1999. Avant cela, C n'avait pas de type booléen mais utilisait plutôt int
pour toutes les expressions booléennes. Par conséquent, tous les opérateurs logiques tels que > == !
etc. renvoient un int
de valeur 1
ou 0
.
Il était courant que les applications utilisent des types faits maison tels que typedef enum { FALSE, TRUE } BOOL;
, qui se résume également à des types de la taille int
.
C++ avait un type booléen bien meilleur et explicite, bool
, qui n'était pas plus grand qu'un 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 dans C avec le standard C99. C a ensuite reçu un type booléen _Bool
ainsi que l'en-tête stdbool.h
.
stdbool.h
fournit 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 type entier petit, probablement 1 octet important. De même, l'en-tête fournit deux macros true
et false
, la même orthographe que les mots clés C++, mais avec une compatibilité ascendante avec les programmes C plus anciens . Par conséquent, true
et false
sont développés en 1
et 0
en C et leur type est int
. Ces macros ne sont pas réellement du type booléen comme le seraient les mots-clés C++ correspondants.
De même, pour des raisons de compatibilité ascendante, les opérateurs logiques de C still retournent un int
à ce jour, même si C a de nos jours 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 int
en C, mais la taille d'un bool
en C++.
En ce qui concerne l'opérateur conditionnel ?:
L'opérateur conditionnel ?:
est un opérateur étrange avec quelques bizarreries. C'est une erreur courante de croire que c'est l'équivalent de if() { } else {}
à 100%. Pas assez.
Il y a un point de séquence entre l'évaluation du premier et du deuxième ou du troisième opérande. Il est garanti que l'opérateur ?:
évalue uniquement le deuxième ou le troisième opérande, de sorte qu'il ne peut exécuter aucun effet secondaire de l'opérande non évalué. Un code tel que true? func1() : func2()
n'exécutera pas func2()
. Jusqu'ici tout va bien.
Cependant , il existe une règle spéciale stipulant que les 2ème et 3ème opérandes doivent être implicitement dactylographiés et comparés les uns aux autres avec les conversions arithmétiques usuelles . ( Règles de promotion de type implicites en C expliquées ici ). Cela signifie que le 2ème ou le 3ème opérande sera toujours toujours au moins égal à int
.
Ainsi, peu importe que true
et false
soient de type int
en C car l'expression donnerait toujours au moins la taille d'un int
, peu importe.
Même si vous réécriviez l'expression en sizeof(a ? (bool)true : (bool)false)
il retournera toujours la taille d'un int
!
Cela est dû à la promotion implicite de types via les conversions arithmétiques habituelles.
Réponse rapide:
sizeof(a ? true : false)
renvoie 4
parce que true
et false
sont définis dans <stdbool.h>
comme 1
et 0
, de sorte que l'expression se développe to sizeof(a ? 1 : 0)
qui est une expression entière de type int
, occupant 4 octets sur votre plate-forme. Pour la même raison, sizeof(true)
aurait également pour résultat 4
sur votre système.Notez cependant que:
sizeof(a ? a : a)
est également évalué à 4
parce que l'opérateur ternaire effectue les promotions d'entier sur ses deuxième et troisième opérandes, s'il s'agit d'expressions entières. La même chose se produit bien sûr pour sizeof(a ? true : false)
et sizeof(a ? (bool)true : (bool)false)
, mais l'expression entière comme bool
se comporte comme prévu: sizeof((bool)(a ? true : false)) -> 1
.
notez également que les opérateurs de comparaison évaluent les valeurs booléennes 1
ou 0
, mais ont int
type: sizeof(a == a) -> 4
.
Les seuls opérateurs qui conservent la nature booléenne de a
seraient:
l'opérateur de virgule: sizeof(a, a)
et sizeof(true, a)
sont évalués à 1
au moment de la compilation.
les opérateurs d'assignation: sizeof(a = a)
et sizeof(a = true)
ont pour valeur 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 type bool
, les valeurs booléennes true
et false
, les opérateurs de comparaison et l’opérateur ternaire: tous ces sizeof()
expressions sont évaluées à 1
en C++.
Voici un extrait de quel est ce qui est inclus dans le source
#ifndef __cplusplus
#define bool _Bool
#define true 1
#define false 0
#else /* __cplusplus */
Les macros true
et false
sont déclarées respectivement 1 et 0.
cependant, dans ce cas, le type est le type des constantes littérales. 0 et 1 sont des constantes entières qui entrent dans un int, leur type est donc int.
et la sizeof(int)
dans votre cas est 4.