飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 编程基础概念

    • 冯·诺依曼体系结构
    • 数据在计算机中的表示
    • 编程语言的层次
    • C语言的起源与发展
    • C99标准的主要改进
    • 开发环境搭建
    • 第一个C程序
    • 编译与运行流程
    • 可移植性风险的三级体系
  • 第2章 数据类型与运算

    • 字符集与标识符
    • 关键字
    • 注释
    • char 类型
    • short 与 int
    • long 与 long long
    • 有符号与无符号
    • 取值范围与 limits.h
    • float 与 double
    • long double
    • _Bool 类型
    • 变量声明与定义
    • 常量
    • 转义序列
    • 算术运算符
    • 赋值运算符
    • 自增自减运算符
    • 关系与判等运算符
    • 逻辑运算符
    • 位运算符
    • 条件运算符
    • 逗号运算符
    • 运算符优先级
    • 隐式类型转换
    • 显式类型转换
  • 第3章 控制流

    • 表达式语句与空语句
    • 复合语句
    • if 语句
    • switch 语句
    • while 循环
    • do-while 循环
    • for 循环
    • break 与 continue
    • goto 语句
    • return 语句
  • 第4章 函数与模块化编程

    • 函数定义
    • 函数声明与原型
    • main 函数
    • 函数调用机制
    • 传值调用
    • 数组参数
    • 作用域
    • 存储期
    • 链接属性
    • static 与 extern
    • 递归
    • 头文件与源文件
    • 头文件保护
    • include 规则
  • 第5章 数组与字符串

    • 一维数组声明与初始化
    • 数组的存储模型
    • 数组访问与越界
    • 数组操作
    • 二维数组
    • 变长数组 VLA
    • 字符串基础
    • 字符串输入输出
    • 字符串处理函数
    • 字符串与数字转换
  • 第6章 指针

    • 指针的概念
    • 指针的声明与使用
    • 指针运算
    • const 与指针
    • 数组名与指针
    • 指针遍历数组
    • 指针与多维数组
    • 指针作为函数参数
    • 函数返回指针
    • 函数指针
    • 二级指针
    • 复杂声明解析
  • 第7章 结构体、联合体与枚举

    • 结构体定义与声明
    • 结构体初始化
    • 结构体成员访问
    • 结构体嵌套
    • 结构体指针
    • 结构体与函数
    • 联合体
    • 联合体与类型双关
    • 枚举类型
    • 位域
    • 内存对齐与填充
  • 第8章 动态内存管理

    • malloc 与 free
    • calloc 与 realloc
    • 内存泄漏
    • 悬垂指针
    • 内存分配策略
    • 自定义内存池
    • Valgrind 与内存检测
    • 内存碎片
    • 内存对齐分配
    • 常见内存错误
  • 第9章 文件输入输出

    • 文件打开与关闭
    • 文本读写
    • 格式化输入输出
    • 二进制读写
    • 文件定位
    • 错误处理
    • 标准流
    • 临时文件
    • 文件操作示例
  • 第10章 预处理器

    • 预处理器基础
    • 宏定义
    • 带参数的宏
    • 条件编译
    • 头文件包含
    • 预定义宏
    • 宏的高级技巧
    • 预处理器陷阱
    • 编译器特定扩展
  • 第11章 标准库概览

    • 标准库概述
    • assert.h
    • ctype.h
    • errno.h
    • float.h
    • limits.h
    • locale.h
    • math.h
    • setjmp.h
    • signal.h
    • stdarg.h
    • stddef.h
    • stdlib.h
  • 第12章 进阶主题

    • 内联函数
    • 变长数组 VLA
    • 复数类型
    • 布尔类型
    • stdint 与 inttypes
    • 灵活数组成员
    • 匿名结构体与联合体
    • 静态断言
    • 线程支持
    • 原子操作

_Bool 类型

C99 引入了 _Bool 类型作为 C 语言的布尔类型,配合 <stdbool.h> 头文件中的 bool、true、false 宏使用。虽然 C 语言长期以来用整数 0 表示"假"、非 0 表示"真",但 _Bool 提供了语义更清晰的布尔运算方式。

基本用法

包含 <stdbool.h> 后,可以使用 bool、true、false:

#include <stdbool.h>
#include <stdio.h>

int main(void)
{
    bool is_ready = true;
    bool is_empty = false;
    
    printf("is_ready = %d\n", is_ready);    /* 输出 1 */
    printf("is_empty = %d\n", is_empty);    /* 输出 0 */
    
    if (is_ready)
        printf("Ready!\n");
    
    return 0;
}

bool 是 _Bool 的别名(通过 typedef),true 定义为 1,false 定义为 0。_Bool 类型的大小是 1 字节(sizeof(_Bool) 为 1),只能存储 0 或 1。

转换规则

任何标量类型赋值给 _Bool 时,非零值转换为 1,零值转换为 0:

#include <stdbool.h>

bool a = 5;         /* 5 非零,a = 1 */
bool b = 0;         /* b = 0 */
bool c = 0.001;     /* 0.001 非零,c = 1 */
bool d = -100;      /* -100 非零,d = 1 */
bool e = NULL;      /* NULL 是 0,e = 0 */

这个规则让 _Bool 能安全地接受任何整数或指针值:

int count = 10;
bool has_items = count;         /* count != 0,has_items = true */

int *ptr = malloc(sizeof(int));
bool valid = ptr;               /* ptr != NULL,valid = true */
free(ptr);
valid = ptr;                    /* ptr 可能非零(悬垂指针),但逻辑上应设为 false */

注意:释放指针后,指针值不一定变为 NULL,所以 valid = ptr 可能仍为 true。应显式赋值:valid = false; 或 ptr = NULL; valid = ptr;。

与整数的区别

在 C99 之前,C 程序员用 int 表示布尔值:

/* 传统 C89 写法 */
int is_valid = 1;       /* 真 */
int is_done = 0;        /* 假 */

if (is_valid) { }

C99 的 _Bool 提供了类型安全:

/* C99 写法 */
bool is_valid = true;
bool is_done = false;

虽然底层仍然是整数(true 是 1,false 是 0),但使用 bool 能明确表达意图,提高代码可读性。

逻辑运算

_Bool 支持所有逻辑和比较运算,结果也是 _Bool:

#include <stdbool.h>

bool a = true;
bool b = false;

bool c = a && b;        /* 逻辑与:false */
bool d = a || b;        /* 逻辑或:true */
bool e = !a;            /* 逻辑非:false */

bool f = (5 > 3);       /* 比较结果:true */
bool g = (5 == 3);      /* false */

逻辑运算符 && 和 || 具有短路求值特性:

bool result = (a && expensive_function());
/* 如果 a 为 false,expensive_function() 不会执行 */

bool result2 = (a || expensive_function());
/* 如果 a 为 true,expensive_function() 不会执行 */

实际应用

函数返回值:

#include <stdbool.h>

bool is_prime(int n)
{
    if (n < 2) return false;
    for (int i = 2; i * i <= n; i++)
        if (n % i == 0) return false;
    return true;
}

if (is_prime(17))
    printf("17 is prime\n");

状态标志:

#include <stdbool.h>

typedef struct {
    bool is_connected;
    bool is_authenticated;
    bool has_data;
} ConnectionState;

ConnectionState conn = {false, false, false};

if (conn.is_connected && conn.is_authenticated)
    printf("Ready to transfer\n");

条件编译中的布尔宏:

#define DEBUG_MODE true

#if DEBUG_MODE
    printf("Debug: x = %d\n", x);
#endif

注意:#if 预处理指令中不能使用 bool、true、false,因为它们在预处理阶段还未定义。应使用整数:#define DEBUG_MODE 1。

常见错误

忘记包含头文件:

/* 错误:未包含 <stdbool.h> */
bool flag = true;       /* 编译错误:bool 未定义 */

/* 正确 */
#include <stdbool.h>
bool flag = true;

如果不包含 <stdbool.h>,可以直接使用 _Bool、_true、_false(不推荐):

_Bool flag = 1;         /* 不使用 <stdbool.h> */

假设 bool 只能存储 true/false:

bool flag = true;
flag = 2;               /* 合法!2 非零,转换为 1 */
printf("%d\n", flag);   /* 输出 1,不是 2 */

_Bool 的存储值只能是 0 或 1,赋值其他值会自动归一化。

与位运算混淆:

bool a = true;
bool b = true;
bool c = a & b;         /* 位运算:1 & 1 = 1,结果 true */
bool d = a && b;        /* 逻辑运算:true && true = true */

虽然对 _Bool 来说 & 和 && 结果相同,但 && 有短路求值,& 没有。对布尔值应始终使用逻辑运算符 &&、||、!。

上一页
long double
下一页
变量声明与定义