为什么 "sizeof(a ? true : false)" 的输出是四个字节?

138

我有一小段关于使用三元运算符的 sizeof 运算符的代码:

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

输出 (GCC):

1
1
4 // Why 4?

但是在这里,

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

三目运算符返回布尔型,在C语言中布尔型的大小为1字节。

那么sizeof(a ? true : false)为什么输出是四个字节呢?


41
sizeof(true)sizeof(false) 也都是4:http://ide.geeksforgeeks.org/O5jvuN - tkausl
7
这里更有趣的问题是为什么这个实现在定义_Bool大小为1时,却没有定义truefalse。但据我所知,标准对此没有任何规定。 - user2371524
12
同样的原因是为什么在C语言中给定“char a;”时,“sizeof(a) == 1”,而“sizeof('a') == sizeof(int)”?这与实现无关,与语言有关。 - n. m.
10
你尝试过打印sizeof(true)吗?也许这会使事情变得更清晰一些(特别是,它将变得明显,三元运算符是一个转移注意力的东西)。 - n. m.
4
@FelixPalmen说的是字面意义。stdbool.htrue定义为#define为1,因此这是其字面定义。 - n. m.
显示剩余11条评论
7个回答

227
这是因为你有#include <stdbool.h>。这个头文件定义了宏truefalse分别等于10,所以你的语句看起来像这样:
printf("%zu\n", sizeof(a ? 1 : 0)); // Why 4?

sizeof(int) 在你的平台上是4。


21
这是因为你有 #include <stdbool.h>。不,不是的。sizeof(a ? (uint8_t)1 : (uint8_t)0);也会得到4的结果。这里重要的是 ?: 操作数的整数提升,而不是 truefalse 的大小。 - Lundin
9
两者都很重要。按照原文,类型已经是 int,没有提升。你不能“修复”它的原因是默认的提升。 - R.. GitHub STOP HELPING ICE
5
这不是C++,而是C语言。在C++中,truefalse不是宏,而是关键字。它们没有被定义为10,而是作为bool类型的真假值。请注意区分。 - Justin
5
不,你今天学到了关于 C 的知识。不要混淆这两种语言。在 C++ 中,sizeof(true) 的结果为 1。演示 - Rakete1111
1
真的,我搞混了。没有仔细阅读,被cppreference链接误导了。是我的错,谢谢。但我对C++有这种感觉。 - Peter Schneider

68

在这里,三元运算符返回布尔类型。

好的,还有更多细节!

C语言中,这个三元运算符的结果为int类型。 [下面是注释(1,2)]

因此,结果与您的平台上的表达式sizeof(int)相同。


注释1:C11,第§7.18章,Boolean type and values <stdbool.h>

[....] 剩下的三个宏适用于#if预处理指令。 它们是

true

展开为整数常量1,

false

展开为整数常量0,[....]

注释2:对于条件运算符,第§6.5.15章,(强调是我的)

先计算第一个操作数;在其评估和第二个或第三个操作数(哪个被评估)之间有一个序列点。只有当第一个操作数不等于0时,才会评估第二个操作数;只有当第一个操作数等于0时,才会评估第三个操作数;结果是第二个或第三个操作数的值(哪个被评估) [...]

以及

如果第二个和第三个操作数都具有算术类型,则通过应用到这两个操作数的通常算术转换确定的结果类型是结果的类型。 [....]

因此,结果将为整数类型,并且由于值范围,常量精确地为int类型。

话虽如此,一个普遍的建议是,int main()应该更改为int main (void)以符合标准。


@user694733 嗯...为什么不行?<stdbool.h>定义了宏的类型为int,这有什么问题吗? - Sourav Ghosh
@BasileStarynkevitch 好的,我现在明白了,这确实是错误的,已经更新了。 - Sourav Ghosh

60

三元运算符是一个转移注意力的东西。

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

打印出4(或者在您的平台上的sizeof(int)是多少)。

以下假设boolchar或类似大小为1的类型的同义词,而intchar大。

sizeof(true) != sizeof(bool)sizeof(true) == sizeof(int)之所以成立,是因为true不是bool类型的表达式,而是int类型的表达式。它在stdbool.h中被定义为1

C中没有bool类型的rvalue。每个这样的rvalue都会立即提升为int,即使作为sizeof的参数时也是如此。 编辑:这段话不正确,sizeof的参数不会被提升为int。但这并不影响任何结论。


好答案。在我阅读了目前最受欢迎的答案后,我认为所有语句都应该评估为4。这澄清了事情。+1 - Pedro A
5
(bool)1不就是类型为bool的右值吗? - Ben Voigt
printf("%u\n", sizeof((char) 1)); 在我的平台上输出 1,而 printf("%u\n", sizeof(1)); 则输出 4。这难道不意味着你的说法“每个这样的 rvalue 都会立即提升为 int,即使用作 sizeof 的参数”是错误的吗? - JonatanE
@Lundin,一旦你知道sizeof(true)==sizeof(int),你是否还会想知道为什么sizeof(1?true:false) == sizeof(int)?这需要单独解释吗? - n. m.
6
我认为这个答案以最佳方式解决了该问题。欢迎您进行点赞或改进它。 - n. m.
显示剩余7条评论

33

关于C语言中的布尔类型

C语言相对较晚才引入布尔类型,具体时间是1999年。在此之前,C语言并不支持布尔类型,而是使用int类型来处理所有布尔表达式。因此,所有逻辑运算符如> == !等返回的都是值为10int类型。

在使用自定义的类型,例如 typedef enum { FALSE, TRUE } BOOL; 的应用程序中,这种情况很常见,它同样是一个int大小的类型。

C++则提供了更好的、明确的布尔类型——bool,其大小不超过1字节。然而在C中,布尔类型或表达式最坏情况下会占用4个字节。在C99标准中,为了与C++兼容,引入了某种程度上的兼容性。C语言也得到了布尔类型 _Bool 和头文件 stdbool.h

stdbool.h 提供了一些与 C++ 兼容的功能。该头文件定义了宏bool(与C++关键字拼写相同),它扩展为 _Bool,一种小整数类型,可能大小为1字节。同样地,该头文件提供了两个宏truefalse,与C++关键字的拼写相同,但向后兼容旧的C程序。因此,在C中,truefalse会扩展为10,其类型是int。这些宏实际上并不像对应的C++关键字那样属于布尔类型。

同样地,为了保持向后兼容性,C语言中的逻辑运算符至今仍会返回一个int类型,尽管现在C语言已经有了布尔类型。而在C++中,逻辑运算符会返回一个bool类型。因此,如sizeof(a == b)这样的表达式在C语言中将给出一个int的大小,但在C++中将给出一个bool的大小。

关于三目运算符?:

三目运算符?:是一个带有一些怪癖的奇怪操作符。很多人错误地认为它与if() { } else {}完全等价。但实际上不是。

在第1个和第2或3个操作数之间存在一个序列点。?:运算符只保证评估第2或第3个操作数,因此它无法执行未被评估的操作数的任何副作用。像true? func1() : func2()这样的代码不会执行func2()。到目前为止还好。

然而,有一个特殊规则规定,第2和第3个操作数必须隐式类型提升并使用通常的算术转换进行平衡。(C语言中的隐式类型提升规则在这里解释)。这意味着第2或第3个操作数将始终至少与一个int类型一样大。

因此,即使在C语言中,truefalse恰好是int 类型,表达式始终会至少给出一个int的大小。

即使您将表达式改写为 sizeof(a ? (bool)true : (bool)false),它仍会返回一个 int 的大小!这是由于通过通常的算术转换进行了隐式类型提升。

1
C++并不保证sizeof(bool)==1 - aschepler
1
@aschepler 不过在 C++ 标准之外的真实世界中确实保证了它。请列出一个编译器,它不是 1。 - Lundin
嗨。我认为这个答案如果去掉第一部分会更好。第二部分回答了问题。其余的内容虽然有趣,但只是噪音。 - YSC
@YSC 这个问题最初被标记为C和C++,因此有必要比较它们不同的bool类型以及它们背后的历史。如果没有C++标签,我怀疑我不会写第一部分。然而,人们必须理解为什么在C中sizeof(bool)为1但sizeof(false)为4。 - Lundin

23
快速答案: sizeof(a ? true : false)的值为4,因为在<stdbool.h>中定义了truefalse分别为10,所以表达式展开为sizeof(a ? 1 : 0),是int类型的整数表达式,在您的平台上占用4个字节。出于同样的原因,在您的系统上,sizeof(true)也将计算为4
请注意: sizeof(a ? a : a)也会计算为4,因为三元运算符对其第二个和第三个操作数执行整数提升,如果这些操作数是整数表达式。 理所当然,sizeof(a ? true : false)sizeof(a ? (bool)true : (bool)false)也是如此,但是将整个表达式转换为bool时的行为与预期相同:sizeof((bool)(a ? true : false)) -> 1
还要注意,比较运算符计算为布尔值10,但具有int类型:sizeof(a == a) -> 4
唯一保持a布尔性质的运算符是:
- 逗号运算符: sizeof(a, a)sizeof(true, a)在编译时都计算为1。 - 赋值运算符: sizeof(a = a)sizeof(a = true)都具有值1。 - 递增运算符: sizeof(a++) -> 1

最后,以上所有内容仅适用于C语言:C++在bool类型、布尔值truefalse、比较运算符以及三元运算符方面具有不同的语义:所有这些sizeof()表达式在C++中都会计算为1


2
优秀的答案实际上指出了 truefalse 是什么类型并不重要,因为 ?: 操作数会被整型提升为 int。因此 sizeof(a ? (uint8_t)true : (uint8_t)false) 的结果也将是 4。 - Lundin
这个答案涵盖了主要的重点,即将值提升为“int”。 - Chinni

1
这是来自源码中的一段代码片段。
#ifndef __cplusplus

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

#else /* __cplusplus */

这里的宏 truefalse 分别声明为 1 和 0。

但是在这种情况下,类型是字面常量的类型。0 和 1 都是适合 int 的整数常量,因此它们的类型是 int。

而你的情况中 int 的大小为 4。


-3

C语言中没有布尔数据类型,而是逻辑表达式在为真时会被计算为整数值1,否则为0

条件表达式如ifforwhilec ? a : b都需要一个整数,如果这个数字非零,则被视为true,除了一些特殊情况。下面是一个递归求和函数,在其中三元运算符将在n达到0之前一直计算为true

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

它还可以用于检查指针的NULL值,这里是一个递归函数,用于打印单链表的内容。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接