5

C语言基本功修炼秘籍第6节,C语言有原生的布尔类型吗?自己将TRUE定义为1安全吗?

 3 years ago
source link: https://blog.popkx.com/c%E8%AF%AD%E8%A8%80%E5%9F%BA%E6%9C%AC%E5%8A%9F%E4%BF%AE%E7%82%BC%E7%A7%98%E7%B1%8D%E7%AC%AC6%E8%8A%82-c%E8%AF%AD%E8%A8%80%E6%9C%89%E5%8E%9F%E7%94%9F%E7%9A%84%E5%B8%83%E5%B0%94%E7%B1%BB/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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语言代码如下,请看:

e6af3d47efb4641b54d50dbdcde3a860.png

包含<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;
}
183eef01105835b8de954a749aa7304d.png

编译并执行这段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 这样的定义值做赋值用,或者做布尔函数的返回值,而尽量避免直接与其他布尔值对比。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK