1 布尔类型
1.1 布尔类型概述
布尔类型用于表示逻辑上的真(true)和假(false)两种状态,是编程中条件判断和逻辑运算的基础。在 C 语言中,布尔值的表示方式随着标准的发展而不断完善。
1.2 布尔类型的三种声明方式
宏定义方式(C89 及以前版本)
- 在 C89 及以前的 C 语言标准中,没有原生的布尔类型。
- 开发者通常使用宏定义来模拟布尔类型,将 TRUE 和 FALSE 分别定义为 1 和 0。
- 这种方式不够直观,但能在一定程度上满足布尔逻辑的需求。
cpp
#include <stdio.h>
// 宏定义布尔类型
#define BOOL int
#define TRUE 1
#define FALSE 0
int main()
{
BOOL isPass = FALSE; // 初始化为假
BOOL isOk = TRUE; // 初始化为真
printf("isPass=%d, isOk=%d\n", isPass, isOk); // 输出: isPass=0, isOk=1
if (isPass)
{
printf("不会执行\n"); // 条件不满足,不会执行
}
if (isOk)
{
printf("会执行\n"); // 条件满足,会执行
}
return 0;
}
程序在 VS Code 中的运行结果如下所示:

_Bool 类型(C99 标准引入)
- C99 标准引入了原生的布尔类型 _Bool。
- _Bool 类型只能存储 0 或 1,任何非 0 值赋给 _Bool 变量都会被隐式转换为 1(真)。
- 这种方式比宏定义更规范,但代码可读性稍差,因为通常需要输出为整数形式。
cpp
#include <stdio.h>
int main()
{
_Bool isPass = 0; // 初始化为假
// _Bool isOk = 1; // 初始化为真
_Bool isOk = -4; // 非 0 值被转换为 1(真)
printf("isPass=%d, isOk=%d\n", isPass, isOk); // 输出: isPass=0, isOk=1
if (isPass)
{
printf("不会执行\n"); // 条件不满足,不会执行
}
if (isOk)
{
printf("会执行\n"); // 条件满足,会执行
}
return 0;
}
程序在 VS Code 中的运行结果如下所示:

bool 类型(通过 stdbool.h 头文件)
- C99 标准还提供了**<stdbool.h> 头文件**,其中定义了 bool 类型作为 _Bool 的别名。
- 同时定义了 true 和 false 宏,分别表示 1 和 0,使得代码更加直观易读。
- 这是现代 C 编程中推荐使用的布尔类型表示方式。
cpp
#include <stdio.h>
#include <stdbool.h> // 包含布尔类型定义
int main()
{
bool isPass = false; // 初始化为假
bool isOk = true; // 初始化为真
printf("isPass=%d, isOk=%d\n", isPass, isOk); // 输出: isPass=0, isOk=1
if (isPass)
{
printf("不会执行\n"); // 条件不满足,不会执行
}
if (isOk)
{
printf("会执行\n"); // 条件满足,会执行
}
return 0;
}
程序在 VS Code 中的运行结果如下所示:

我们可以通过 VS Code 查看 <stdbool.h> 的源代码。将鼠标放在 stdbool.h 或 true 或 false 上,按住 Ctrl 键并点击鼠标左键即可查看其实现。<stdbool.h> 的源代码片段通常如下:

这进一步验证了 bool 是 _Bool 的别名,true 和 false 分别对应 1 和 0。
1.3 三种声明方式的执行时间
宏定义方式
- 执行时间:
- 宏定义是在预处理阶段执行的。预处理器会在编译之前处理所有的宏定义、文件包含等指令。
- 在预处理阶段,所有的宏定义会被替换为对应的文本。例如,BOOL 会被替换为 int,TRUE 会被替换为 1,FALSE 会被替换为 0。
- 特点:
- 宏定义不会引入新的类型,只是简单的文本替换。
- 由于是在预处理阶段完成,因此在编译阶段,编译器看到的已经是替换后的代码。
_Bool 类型
- 执行时间:
- _Bool 类型的处理是在编译阶段完成的。
- 编译器会识别 _Bool 类型,并在生成目标代码时进行相应的处理。
- 特点:
- _Bool 是 C 语言原生的布尔类型,提供了更好的类型安全性。
- 编译器会在编译阶段对 _Bool 类型的变量进行优化,确保其只存储 0 或 1。
bool 类型
- 执行时间:
- bool 类型的处理也是在编译阶段完成的。
- 当包含 <stdbool.h> 头文件时,预处理器会将 bool 替换为 _Bool,true 替换为 1,false 替换为 0。
- 编译器在编译阶段会识别 _Bool 类型,并进行相应的处理。
- 特点:
- bool 是 _Bool 的别名,提供了更直观和可读的布尔类型定义。
- 使用 <stdbool.h> 是现代 C 编程中推荐的方式,因为它提高了代码的可读性和一致性。
执行时间总结
方式 | 处理阶段 | 特点 |
---|---|---|
宏定义 | 预处理阶段 | 简单文本替换,无类型检查,灵活性高但安全性低 |
_Bool 类型 | 编译阶段 | 原生布尔类型,类型安全,编译器优化 |
bool 类型 | 编译阶段 | _Bool 的别名,通过 <stdbool.h> 提供,直观可读,推荐使用 |
1.4 布尔类型的赋值规则
接受任何整数值
- 布尔变量可以接受任何整数值(如 char、short、int、long 等类型)或任何可隐式转换为整数的表达式(如算术运算、位运算、比较运算等)。
- 赋值时,编译器会自动将值转换为 0(false)或 1(true),无需手动处理。
非零值视为 true(存储为 1)
- 任何非零值(包括正整数、负整数、大整数、甚至超出 bool 存储范围的值)都会被视为 true,并存储为 1。
- bool 的存储大小是固定的(通常为 1 字节,即 _Bool 类型),但赋值时不会因值过大而报错或截断。
- 编译器会自动转换:
- 无论赋值 1、42 还是 1000000,最终存储的值只能是 0 或 1。
- 这是因为 bool 类型的本质是逻辑值,而非数值,编译器会隐式地将任何非零值转换为 1。
cpp
#include <stdio.h>
#include <stdbool.h>
int main()
{
bool b1 = 42; // 42 → true(存储为 1)
bool b2 = -1; // -1 → true(存储为 1)
bool b3 = 1000000; // 1,000,000 → true(存储为 1)
printf("b1=%d (实际存储值: %d)\n", b1, b1); // 输出: b1=1 (实际存储值: 1)
printf("b2=%d (实际存储值: %d)\n", b2, b2); // 输出: b2=1 (实际存储值: 1)
printf("b3=%d (实际存储值: %d)\n", b3, b3); // 输出: b3=1 (实际存储值: 1)
return 0;
}
程序在 VS Code 中的运行结果如下所示:

零值视为 false(存储为 0)
- 只有 0 或显式的 false 会被视为 false,并存储为 0。
cpp
#include <stdio.h>
#include <stdbool.h>
int main()
{
bool b4 = 0; // 0 → false(存储为 0)
bool b5 = false; // false → false(存储为 0)
printf("b4 = %d\n", b4); // 0
printf("b5 = %d\n", b5); // 0
return 0;
}
程序在 VS Code 中的运行结果如下所示:

规则总结
规则 | 示例 | 存储值 | 说明 |
---|---|---|---|
非零值 → true(1) | bool b = 42; | 1 | 任何非零值(包括负数、大数) |
零值 → false(0) | bool b = 0; | 0 | 仅 0 或 false |
隐式转换 | bool b = -1; | 1 | 编译器自动处理非零值 |
- bool 类型仅存储 0 或 1,赋值时会自动转换,无需担心数值大小。
- 这一规则确保了布尔逻辑的简洁性和一致性。
2 sizeof 运算符
sizeof 是 C 语言中的一个重要运算符,用于获取数据类型、变量、字面量或表达式的存储大小(以字节为单位)。
它返回一个 size_t 类型的无符号整数值 ,通常使用 %zu 格式占位符进行输出。size_t 的具体类型(如 unsigned int 或 unsigned long)由系统和编译器决定。
2.1 sizeof 运算符的基本用法
sizeof 运算符的主要功能是计算内存占用大小,适用于以下场景:
- **基本数据类型:**如 bool、char、short、int、long、long long、float、double、long double 等。
- **变量:**直接作用于变量名。
- **字面量:**直接作用于常量值。
- **表达式:**计算表达式的存储大小。
语法形式:
- 对于基本数据类型或表达式:sizeof 后面必须使用括号包裹,例如 sizeof(int)、sizeof(1 + 1)。
- 对于变量或字面量:sizeof 后面的括号是可选的,例如 sizeof a 或 sizeof(a)。
2.2 sizeof 的使用场景
计算基本数据类型的大小
基本数据类型的大小可能因编译器和平台而异。以下是常见数据类型的典型大小:
cpp
#include <stdio.h>
#include <stdbool.h> // 包含 bool 类型
int main()
{
// 使用转义字符 \t 格式化输出
printf("数据类型\t大小(字节)\n");
printf("--------\t-----------\n");
// 对于基本数据类型:sizeof 必须使用括号包裹
printf("bool \t\t %zu \n", sizeof(bool)); // 通常为 1 字节
printf("char \t\t %zu \n", sizeof(char)); // 通常为 1 字节
printf("short \t\t %zu \n", sizeof(short)); // 通常为 2 字节
printf("int \t\t %zu \n", sizeof(int)); // 通常为 4 字节
printf("long \t\t %zu \n", sizeof(long)); // 通常为 4 或 8 字节
printf("long long \t %zu \n", sizeof(long long)); // 通常为 8 字节
printf("float \t\t %zu \n", sizeof(float)); // 通常为 4 字节
printf("double \t\t %zu \n", sizeof(double)); // 通常为 8 字节
printf("long double:\t %zu \n", sizeof(long double)); // 通常为 16 字节
return 0;
}
程序在 VS Code 中的运行结果如下所示:

计算字面量的大小
字面量的大小取决于其类型,sizeof 后面的括号是可选的:
- **字符字面量(如 'a'):**通常被提升为 int 类型,因此 sizeof('a') 返回 4(字节)。
- **整数和浮点数字面量:**大小由其类型决定。例如:
- 431 是 int 类型,sizeof(431) 返回 4。
- 4.31 是 double 类型,sizeof 4.31 返回 8。
cpp
#include <stdio.h>
#include <stdbool.h>
int main()
{
// 字符类型字面量:在 C 中字符常量如 'a' 实际上是 int 类型(通常为 4 字节)
printf("字符类型字面量('a'):%zu\n", sizeof('a')); // 输出通常为 4(bool 提升为 int)
printf("布尔类型字面量(true):%zu\n", sizeof(true)); // 输出通常为 4(bool 提升为 int)
printf("布尔类型字面量(false):%zu\n", sizeof(false)); // 输出通常为 4(bool 提升为 int)
// 整型字面量:默认是 int 类型
printf("整型字面量(431):%zu\n", sizeof(431)); // 输出通常为 4(int)
// 长整型后缀 L:long int(具体大小依赖平台)
printf("长整型字面量(431L):%zu\n", sizeof(431L)); // 具体大小依赖平台
// 长长整型后缀 LL:long long int(通常为 8 字节)
printf("长长整型字面量(431LL):%zu\n", sizeof(431LL)); // 输出通常为 8(long long)
// 单精度浮点数字面量:使用 f 后缀表示 float(通常为 4 字节)
printf("单精度浮点型字面量(4.31f):%zu\n", sizeof(4.31f)); // 输出通常为 4(float)
// 双精度浮点数字面量:默认是 double 类型(通常为 8 字节)
printf("双精度浮点型字面量(4.31):%zu\n", sizeof(4.31)); // 输出通常为 8(double)
// 长双精度浮点数字面量:使用 L 后缀表示 long double(通常为 16 字节)
printf("长双精度浮点型字面量(4.31L):%zu\n", sizeof(4.31L)); // 输出通常 16
// 对于字面量:sizeof 后面的括号是可选的
printf("对于字面量:sizeof 后面的括号是可选的:%zu\n", sizeof true); // 输出通常为 4(bool 提升为 int)
printf("对于字面量:sizeof 后面的括号是可选的:%zu\n", sizeof 431); // 输出通常为 4(int)
printf("对于字面量:sizeof 后面的括号是可选的:%zu\n", sizeof 431L); // 具体大小依赖平台
printf("对于字面量:sizeof 后面的括号是可选的:%zu\n", sizeof 431LL); // 输出通常为 8(long long)
printf("对于字面量:sizeof 后面的括号是可选的:%zu\n", sizeof 4.31f); // 输出通常为 4(float)
printf("对于字面量:sizeof 后面的括号是可选的:%zu\n", sizeof 4.31); // 输出通常为 8(double)
printf("对于字面量:sizeof 后面的括号是可选的:%zu\n", sizeof 4.31L); // 输出通常为 16(long double)
return 0;
}
程序在 VS Code 中的运行结果如下所示:

计算变量的大小
变量的大小与其类型一致,sizeof 后面的括号是可选的:
cpp
#include <stdio.h>
int main()
{
char a = 'A'; // char 类型变量
short b = 66; // short 类型变量
int c = 66; // int 类型变量
long d = 8888888L; // long 类型变量
long long e = 99999999LL; // long long 类型变量
float f = 5.6f; // float 类型变量
double g = 10.8; // double 类型变量
long double h = 12.34L; // long double 类型变量
printf("a: %zu \n", sizeof(a)); // 输出为 1(char 类型)
printf("b: %zu \n", sizeof(b)); // 输出为 2(short 类型)
printf("c: %zu \n", sizeof(c)); // 输出为 4(int 类型)
printf("d: %zu \n", sizeof(d)); // 具体大小依赖平台
// 对于变量:sizeof 后面的括号是可选的
printf("e: %zu \n", sizeof e); // 输出为 8(long long 类型)
printf("f: %zu \n", sizeof f); // 输出为 4(float 类型)
printf("g: %zu \n", sizeof g); // 输出为 8(double 类型)
printf("h: %zu \n", sizeof h); // 输出为 16(long double 类型)
return 0;
}
程序在 VS Code 中的运行结果如下所示:

计算表达式的大小
sizeof 也可以用于计算表达式的存储大小:
cpp
#include <stdio.h>
int main()
{
int d1 = 10;
double d2 = 20.0;
// printf("表达式(d1 + d2):%zu \n", sizeof d1 + d2); // 不加括号这里输出会出问题,
printf("表达式(d1 + d2)的存储大小:%zu \n", sizeof(d1 + d2)); // 加上括号当成一个整体
// int 类型和 double 类型相加,结果是 double 类型,所以 sizeof(d1 + d2) 的结果是 8
printf("表达式(1 + 2)的存储大小:%zu \n", sizeof(1 + 2)); // 4
return 0;
}
程序在 VS Code 中的运行结果如下所示:

2.3 注意事项
-
括号的可选性:
- 对基本数据类型和表达式必须使用括号(如 sizeof(int))。
- 对变量或字面量可省略括号(如 sizeof a)。
- **建议:**为了统一规范和代码可读性,建议在使用 sizeof 时,始终使用括号,无论是对数据类型、变量、字面量还是表达式。例如:sizeof(int)、sizeof(variableName)、sizeof(42)。
类型提升:
- 字符字面量(如 'a')在 sizeof 运算中会被提升为 int 类型。
-
平台和编译器依赖性:
- 数据类型的大小可能因平台和编译器而异(如 long 可能是 4 或 8 字节、long double 可能是 8 字节、10 字节、12 字节或 16 字节)。