C语言程序开发是离不开条件判断语句的,有程序员甚至认为编程其实就是 if else 再加上一点数学计算。不过,对于计算机来说,条件判断结果只有两个可能值:真 或者 假。
C语言认为所有非零值都是“真”,零值为“假”,虽然很简单,但是从读者向我反馈的问题来看,这中间仍然有一些让初学者迷惑的地方,为此,本节将以问答形式讨论一下C语言程序开发中的布尔值以及相关表达式。
C语言中有标准的布尔(Boolean)类型吗?
首先来说说什么是布尔类型。狭义来看,布尔类型是只有“真”和“假”两个可能值的类型,鉴于目前计算机只认识 0 和 1,这里认为布尔类型只有 0 和 1 两个可能值的类型。
经典C语言是没有提供标准布尔类型的,程序员如果希望使用布尔类型,一般都是使用其他数据类型代替,比如 int ,char ,枚举等类型,或者干脆直接使用 0 和 1:
#define TRUE 1
#define FALSE 0
#define bool int
#define bool char
enum bool {false, true};
一般来说,使用 int 类型程序的效率更高一些,而使用 char 类型,程序消耗的内存空间更小一些。当然了,如果读者的调试器能够显示枚举名,枚举类型可能更方便于开发调试。
也有程序员更加偏爱下面这种定义方式,请看:
#define TRUE (1==1)
#define FALSE (!TRUE)
值得说明的是,C99 增加了_Bool
关键字,使得C语言有了自己的布尔类型。C99 新增了标准头文件 <stdbool.h>
,它的内容很简单,相关C语言代码如下,请看:
包含<stdbool.h>
头文件后,就可以使用 bool 定义布尔类型变量了,例如:
#include <stdbool.h>
bool b;
b = true;
b = false;
上述C语言代码中的变量 b 即为布尔类型,它只能存储 0 或者 1。下面是较为完整的使用示例:
#include <stdbool.h>
#include <stdio.h>
int main()
{
bool test;
test = 0;
test = 1;
test = 2;
printf("sizeof(bool)=%lu, test=%d\n", sizeof(bool), test);
return 0;
}
编译并执行这段C语言代码,得到如下输出:
# gcc t3.c
# ./a.out
sizeof(bool)=1, test=1
可见,编译器是允许使用除 1 外的其他非零值对布尔类型变量 test 赋值的,但是最终编译器会将该非零值转换为 1。另外,bool 类型也要占用 1 字节的内存空间。
在C语言程序开发中,将 TRUE 定义为 1 危险吗?
有读者问我,既然C语言将“所有非零值”都当作“真”,那么在程序中将 TRUE 定义为 1 会不会有局限性呢?
读者有这样的顾虑,应该是考虑到了“不同的真值”对比将得到错误答案。例如同为真的值 3 和 8,却不能像 “真==真”这样对比,因为“3==8”明显是“假”的,C语言中的真假值好像有些混乱。
C语言的确将所有非零值都当作“真”,但是这仅适用于“输入”时,即需要布尔值的地方。诸如 ==, !=, < 等逻辑运算符处理后的布尔值必定是 0 或者 1,因此像:
if( (a==b)==TRUE )
这样的C语言代码必定可以正常工作,但是这样写代码无疑是愚蠢的。事实上,在C语言程序中,直接比较真假值的确非常危险,再举例来说,某些库函数(如 isupper(), isalpha() 等)成功时返回的是非零值,不一定是 1,下面这样的C语言代码就比较危险了:
if(isupper(...)==TRUE)
可能得不到正确的判断结果。所以,在C语言程序开发中,将 TURE 定义为 1 危险不危险,取决于程序员的使用方式。我比较推荐只将 TRUE 这样的定义值做赋值用,或者做布尔函数的返回值,应该特别小心用其做比较。
如果必须要比较布尔值,我在C语言程序开发中,常使用的一个小技巧是使用 !!,例如 if( (!!a) == (!!b) ),原因留给读者自己考虑了。
有程序员认为:
if( (a==b)==TRUE )
是下面这种写法的提升,程序将更加安全:
if( a==b )
如果真的是这样,为什么只写一个 TRUE,不多写几个呢?
if( (((a==b)==TRUE)==TRUE)==TRUE )
需要判断 p 是否真的时候,直接写 if(p) 会丢失可移植性吗?如果 p 是指针呢?
当然是可以直接写成 if(p) 。如果 p 不等于 0,那么C语言将认为 if(p) 成立,否则认为 if(p) 不成立。事实上,C语言编译器会将 if(p) 等同于 if( p!=0 )。
可能读者已经知道,C语言中的空指针值有时候并不等于 0。那么,此时 if(p) 还能成功判断 p 是否空指针吗?答案是肯定的。C语言编译器比较聪明,它在处理代码时是会考虑上下文的。
以 if(p) 为例,其等同于 if(p!=0),编译器发现 p 是一个指针,于是会进一步将 0 看作是空指针,替换成相应的空指针值,所以即使 p 是指针,if(p) 仍然能够正常工作。
小结
C语言认为“所有非零值都为真”,这造成了一些初学者理解布尔值的混乱,好在C99提供了原生的布尔类型。不过,如果读者在不支持C99的平台开发程序,只能自定义布尔类型及布尔值,可以考虑遵守本节提供的经验:只将 TRUE 这样的定义值做赋值用,或者做布尔函数的返回值,而尽量避免直接与其他布尔值对比。