第1章:基本数据类型与变量
各位同学,咱们今天聊聊C语言里最基础的东西——数据类型和变量。你别看它基础,我做了十几年嵌入式开发,见过太多老手在这上面翻车。说白了,数据就是程序的血液,类型就是血液的标签。标签贴错了,程序跑起来就会出各种怪问题。
1.1 整型(int)——最常用的数据容器
整型,就是用来存整数的。在C语言里,int是最常用的整型类型。但有个坑我得先告诉你:int的字节长度其实跟平台有关。
关键点:在16位系统上,int是2字节;在32位和64位系统上,int通常是4字节。
我在做STM32项目时,就遇到过把int当4字节用,结果移植到16位MCU上直接数据溢出的情况。嗯,那次排查了整整一个下午。
整型还有几个变种:
short int(或short)—— 通常2字节,范围小但省内存long int(或long)—— 至少4字节,范围更大long long—— C99标准引入,至少8字节
你想想看,如果只是存个年龄,用int就够了。但要是存时间戳,就得用long long。选类型不是越宽越好,得看实际需求。
1.2 浮点型(float/double)——处理小数
浮点型用来表示带小数点的数。C语言提供了两种:
| 类型 | 字节数 | 有效位数 | 典型范围 |
|---|---|---|---|
| float | 4 | 约6-7位 | ±1.2E-38 ~ ±3.4E+38 |
| double | 8 | 约15-16位 | ±2.3E-308 ~ ±1.7E+308 |
我个人习惯是:能用double就不用float。为什么?因为float的精度在累加运算中很容易丢失。我曾经在做一个传感器数据滤波算法时,用float累加1000次后,误差已经大到不可接受了。换成double后,问题迎刃而解。
注意:浮点数不能直接用 == 比较相等!因为浮点运算有精度误差。正确的做法是判断两个数的差是否小于一个很小的阈值(比如 0.00001)。
1.3 字符型(char)——不只是字符
char在C语言里是个很有意思的类型。它名义上是字符型,实际上就是个1字节的整数。你完全可以拿它来存小范围的整数。
char ch = 'A'; // 字符A,实际存的是65(ASCII码)
char num = 100; // 合法!char可以当小整数用
char flag = 0; // 用来表示开关状态
这里有个细节:char到底是signed还是unsigned,C标准没规定,由编译器决定。我建议你明确写:
signed char // 范围 -128 ~ 127
unsigned char // 范围 0 ~ 255
我在做通信协议解析时,经常用unsigned char来存原始字节流。因为协议里的数据都是0~255,用unsigned char最合适,不会出现负数这种奇怪的值。
1.4 变量定义与初始化——好习惯从第一天养成
变量定义很简单:
int count;
float temperature;
char key;
但我要强调一个原则:变量一定要初始化。C语言不会自动帮你清零局部变量,它的初始值是随机的(栈上的垃圾数据)。
我的习惯:定义变量的同时就初始化,哪怕只是赋个0。这能避免90%的"莫名其妙"的bug。
int count = 0;
float temperature = 0.0f;
char key = '\0';
你想想看,如果一个变量没初始化就拿来用,程序的行为就是未定义的。未定义意味着什么?今天能跑,明天可能就崩了。我曾经接手过一个项目,里面有个变量没初始化,在调试版上跑得好好的,发布版就随机死机。查了三天才发现是这个问题。
1.5 常量——不变的值
C语言里定义常量有两种方式:
1.5.1 #define 宏定义
#define PI 3.14159
#define MAX_BUFFER_SIZE 1024
#define是预处理器指令,在编译前就把代码里的PI替换成3.14159。它不占内存,没有类型检查。
1.5.2 const 关键字
const double PI = 3.14159;
const int MAX_BUFFER_SIZE = 1024;
const是真正的常量,有类型,编译器会做类型检查。我个人更推荐用const,因为类型检查能帮你提前发现错误。
经验之谈:在嵌入式开发中,我习惯把硬件相关的常量用#define定义(比如引脚号、寄存器地址),把算法相关的常量用const定义。这样代码更清晰,也更容易维护。
1.6 sizeof运算符——测量数据大小
sizeof是C语言的一个运算符,不是函数。它用来计算变量或类型占用的字节数。
printf("int 的大小: %zu 字节\n", sizeof(int));
printf("float 的大小: %zu 字节\n", sizeof(float));
printf("double 的大小: %zu 字节\n", sizeof(double));
printf("char 的大小: %zu 字节\n", sizeof(char));
int arr[10];
printf("数组的大小: %zu 字节\n", sizeof(arr)); // 40字节(假设int是4字节)
printf("数组元素个数: %zu\n", sizeof(arr) / sizeof(arr[0])); // 10
这里有个经典用法:用sizeof计算数组元素个数。这样写的好处是,就算你改了数组大小,代码也不用改。
注意:sizeof对指针求大小,得到的是指针本身的大小(4或8字节),不是它指向的数据的大小。这是新手最容易犯的错误之一。
我曾经在面试时问过一个候选人:sizeof("Hello")是多少?他回答5。实际上答案是6,因为字符串末尾还有个'\0'。你看,这种细节不注意,写出来的代码就会有隐患。
知识体系总览
下面这张图帮你理清本章的知识结构:
这张图把本章内容分成了五大块:整型、浮点型、字符型、变量与常量、sizeof。每一块都有它的应用场景和注意事项。你学完这一章,应该能回答这几个问题:
- 我的数据需要多大范围?该用哪种类型?
- 变量要不要初始化?不初始化会怎样?
- 用#define还是用const?什么时候用哪个?
- sizeof能帮我做什么?
好了,这一章的内容就到这里。记住我反复强调的那句话:选对类型,初始化变量,注意精度与范围。这三个习惯养成了,你写出来的C代码就会稳很多。
公众号:蓝海资料掘金营,微信deep3321