3. 基本数据类型(下):浮点型、字符型、布尔型与sizeof
好,咱们接着聊。上一节我们把整数型聊透了,这一节我带你看看剩下的几个“常客”——浮点型、字符型、还有那个很多人容易忽略的布尔型。最后,我会教你一个特别实用的工具:sizeof。
说实话,我当年刚学C语言时,觉得浮点数不就是带小数点的数嘛,有啥好讲的?结果第一次做嵌入式项目,就被精度问题坑了一把。嗯,咱们慢慢说。
3.1 浮点型:float 与 double
浮点型,说白了就是用来存小数的。C语言里主要有两种:float 和 double。
| 类型 | 字节数 | 有效位数 | 典型范围 |
|---|---|---|---|
| float | 4 字节 | 约 6~7 位 | ±1.2e-38 ~ ±3.4e38 |
| double | 8 字节 | 约 15~16 位 | ±2.2e-308 ~ ±1.8e308 |
你可能会问:为什么叫“浮点”?因为小数点可以浮动。比如 3.14 和 314.0,在内存里其实是用科学计数法存的,指数部分决定了小数点的位置。
核心区别:float 省内存,但精度低;double 精度高,但占空间。在单片机上,我一般优先用 float,除非对精度有硬性要求。
举个例子:
float pi = 3.14159f; // 注意后面的 f,表示 float 字面量
double pi_d = 3.141592653589793;
这里有个细节:如果你写 3.14159 不加 f,编译器默认它是 double 类型。赋值给 float 时会有隐式转换,虽然大多数情况没问题,但我建议你养成加 f 的习惯。
注意:浮点数不能直接用 == 比较相等!因为精度问题,0.1 + 0.2 可能不等于 0.3。我曾经在项目里用 if (a == b) 判断两个浮点数,结果程序时灵时不灵,查了一天才发现是精度问题。正确的做法是判断差值是否小于某个很小的数,比如 fabs(a - b) < 1e-6。
3.2 字符型:char
字符型 char 在 C 语言里其实是个“两面派”。它本质上是一个整数,但通常用来表示字符。
你看:
char ch = 'A'; // 字符 A
char code = 65; // 也是 A,因为 ASCII 码中 65 对应 'A'
这两行代码效果完全一样。为什么?因为 char 就是 1 字节的整数,范围 -128 到 127(如果是有符号),或者 0 到 255(如果是无符号)。
我个人习惯用 char 存字符,用 unsigned char 存小范围整数或原始字节数据。比如在通信协议里,我经常用 unsigned char 来存接收到的数据包。
小技巧:如果你不确定 char 在某个平台上是有符号还是无符号,可以用 char c = 0xFF; if (c > 0) ... 来测试。不过更稳妥的做法是显式声明 signed char 或 unsigned char。
3.3 布尔型:_Bool
C99 标准引入了 _Bool 类型,专门用来表示真/假。它只有两个值:0 表示假,非 0 表示真。
不过说实话,_Bool 这个名字有点丑。所以标准库提供了 stdbool.h,里面定义了 bool、true、false 三个宏。我建议你直接用这个:
#include <stdbool.h>
bool is_ready = true;
if (is_ready) {
// 执行操作
}
你想想看,用 bool 比用 int flag = 1 要清晰得多。代码的可读性一下子就上来了。
注意:_Bool 其实只占 1 字节,但如果你把它放在结构体里,可能会因为对齐产生额外的空间开销。我在做嵌入式开发时,如果内存特别紧张,有时会用 unsigned int 的某一位来存布尔值,但那是极端情况了。
3.4 数据类型的大小:sizeof
sizeof 是 C 语言的一个运算符,不是函数。它用来计算某个类型或变量占用的字节数。
用法很简单:
printf("int 的大小: %zu 字节\n", sizeof(int));
printf("float 的大小: %zu 字节\n", sizeof(float));
int arr[10];
printf("数组大小: %zu 字节\n", sizeof(arr)); // 40 字节(假设 int 占 4 字节)
这里有个坑:sizeof 对数组名求值时,得到的是整个数组的大小,而不是指针的大小。但如果你把数组名传给函数,它就退化为指针了,这时候 sizeof 得到的是指针的大小。
我曾经踩过的坑:写了一个函数,参数是 int arr[],然后在函数里用 sizeof(arr) / sizeof(arr[0]) 来算数组长度。结果死活不对。后来才意识到,函数参数里的 int arr[] 其实就是 int *arr,sizeof(arr) 返回的是指针大小(4 或 8 字节),而不是数组大小。所以,数组长度最好在外面算好再传进来。
另外,sizeof 的结果类型是 size_t,用 %zu 格式化输出。有些老编译器可能不支持 %zu,那就用 %lu 或强制类型转换。
3.5 知识体系总览
下面这张图帮你梳理一下这一节的核心内容:
3.6 小结
这一节我们聊了浮点型、字符型、布尔型,还有那个看似简单但容易出错的 sizeof。说白了,这些类型都是你写 C 程序时天天要打交道的。理解它们的本质——浮点数是科学计数法,字符数是整数,布尔数就是 0 或非 0——你就能避免很多低级错误。
嗯,我记得刚入行时,带我的老工程师跟我说过一句话:“C 语言的数据类型,就像工具箱里的螺丝刀。你得知道哪个是十字的,哪个是一字的,用错了就会滑丝。” 这么多年过去了,我觉得这话一点没错。