if 语句
以下引用自"The C Programming Language 2nd Edition"
从描述中可以概括为:
- 0为假
- 非0为真
相信这也是大家(包括我自己在内)习以为常的“真理”。
特别要注意的是,在C语言中,并没有bool类型。
到了C++中,在语言层面已经有bool类型了。上述中“0为假,非0为真”在大多数情况下还是适用的,但是也有例外的情况,使得上述“真理”在某些情况下就不成立了。
出现条件
- 未初始化的bool类型变量,搭配
- 逻辑非 !
实验
little endian,x86_64,sizeof(int) = 4
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
void f1(void *p)
{
bool *b = (bool *)p;
if (!*b)
{
printf("!bool: true\n");
}
else
{
printf("!bool: false\n");
}
}
void f2(void *p)
{
uint8_t *b = (uint8_t *)p;
if (!*b)
{
printf("!uint8_t: true\n");
}
else
{
printf("!uint8_t: false\n");
}
}
int main(int argc, char **argv)
{
int i = atoi(argv[1]);
uint8_t *c = (uint8_t *)&i;
printf("0x%02x ", *c++);
printf("0x%02x ", *c++);
printf("0x%02x ", *c++);
printf("0x%02x\n", *c++);
f1(&i);
f2(&i);
return 0;
}
运行
读取命令行输入的参数,模拟未初始化的内存区域为指定的值。
其中,case1和case2符合我们对于“0为假,非0为真”的认知。
case3中,对于int的打印,也符合这个认知。但是对于bool的打印却出乎意料(因为2为真,!2应该是假)。
case1
./bool 0
0x00 0x00 0x00 0x00
!bool: true
!uint8_t: true
case2
./bool 1
0x01 0x00 0x00 0x00
!bool: false
!uint8_t: false
case3
./bool 2
0x02 0x00 0x00 0x00
!bool: true
!uint8_t: false
解释
对比示例代码中的f1和f2函数生成的汇编代码:
可以看到,对于bool类型变量的逻辑非操作,有一条xor的汇编指令:
xorl $1, %eax
该指令只对eax中的最后一个bit进行取反,对于其它bit原样保留。
在case3中,对于输入的数值2,即eax=2,其2进制表示为:
00000010
对最后一个bit取反后为:
00000011
testb %al, %al
该指令测试al(eax的低8位)是否为0,显然 3 != 0,因此打印“!bool: true”。
总结
- “0为假,非0为真”并不是绝对的“真理”
- 在C++中,bool类型一定要初始化