数据类型(Data Types)
数据类型决定了变量"能存什么值"以及"占多少内存"。选择正确的类型是编写正确、高效代码的第一步。
1. 为什么需要数据类型
计算机中所有数据最终都是 0 和 1。数据类型告诉编译器:
- 占多大空间:
int通常占 4 字节,char占 1 字节。 - 如何解释:同一段二进制,当作
int是整数,当作float是小数。 - 允许什么操作:
int可以做取模(%),float不能直接取模。
内存中的同一段二进制 01000000 01001000 11110101 11000011:
解释为 int → 1078523331
解释为 float → 3.14
解释为 char[4] → '@' 'H' '?' '?'
2. 基本数据类型一览
| 类型 | 关键字 | 典型大小 | 取值范围 | 用途 |
|---|---|---|---|---|
| 字符型 | char |
1 字节 | -128 ~ 127 或 0 ~ 255 | 存储 ASCII 字符 |
| 短整型 | short |
2 字节 | -32768 ~ 32767 | 小范围整数 |
| 整型 | int |
4 字节 | -2,147,483,648 ~ 2,147,483,647 | 常规整数 |
| 长整型 | long |
4 或 8 字节 | 平台相关 | 较大范围整数 |
| 长长整型 | long long |
8 字节 | ±9.22×10¹⁸ | 极大整数 |
| 单精度浮点 | float |
4 字节 | ±3.4×10³⁸,6~7 位有效数字 | 普通小数 |
| 双精度浮点 | double |
8 字节 | ±1.7×10³⁰⁸,15~16 位有效数字 | 高精度小数 |
| 无类型 | void |
— | — | 表示"无返回值"或"无参数" |
注意:
int和long的具体大小取决于编译器和平台。可用sizeof运算符查看实际大小。
3. 深入理解各类型
3.1 整数类型(Integer Types)
char c = 'A'; // 字符本质是小整数,'A' 的 ASCII 值是 65
short s = 32767; // 适合小范围计数
int i = 42; // 默认的整数类型,日常首选
long l = 100000L; // 后缀 L 表示 long 字面量
long long ll = 9000000000LL; // 后缀 LL 表示 long long
无符号(unsigned)变体:将符号位也用来表示数值,范围扩大一倍但只能表示非负数。
| 类型 | 有符号范围 | 无符号范围 |
|---|---|---|
char / unsigned char |
-128 ~ 127 | 0 ~ 255 |
int / unsigned int |
±21 亿 | 0 ~ 42 亿 |
short / unsigned short |
±32767 | 0 ~ 65535 |
unsigned int count = 100; // 明确表示"计数"——不可能是负数
unsigned char byte = 0xFF; // 表示一个原始字节,用于底层操作
3.2 浮点类型(Floating-Point Types)
浮点数用科学计数法的二进制形式存储,因此存在精度限制。
float f = 3.1415926535f; // 后缀 f 表示 float 字面量
double d = 3.1415926535; // 默认的小数字面量是 double 类型
精度差异示例:
float a = 1.0f / 3.0f; // 0.3333333(约 7 位有效数字)
double b = 1.0 / 3.0; // 0.3333333333333333(约 15 位有效数字)
浮点数不能精确表示所有十进制小数(如 0.1 在二进制中是无限循环小数),因此不要用 == 直接比较两个浮点数是否相等:
float x = 0.1f + 0.2f;
if (x == 0.3f) { // 可能为假!
// 不可靠
}
// 正确做法:判断差值是否在可接受范围内
float epsilon = 0.000001f;
if (fabs(x - 0.3f) < epsilon) {
// 认为近似相等
}
3.3 字符类型(char)
char 本质上是一个 1 字节的整数,ASCII 字符只是它的"人类可读"形式。
char ch1 = 'A'; // ch1 的实际值是 65
char ch2 = 65; // 等价于 'A',但不推荐这样写
printf("%c\n", ch1); // 输出:A
printf("%d\n", ch1); // 输出:65
| 转义字符 | 含义 |
|---|---|
\n |
换行 |
\t |
制表符(Tab) |
\\ |
反斜杠本身 |
\' |
单引号 |
\" |
双引号 |
\0 |
空字符(字符串结束标志) |
3.4 void 类型
void 表示"没有类型",主要用于三种场景:
void func() { ... } // 函数不返回任何值
int rand(void) { ... } // 函数不接受任何参数(显式声明)
void* ptr = malloc(100); // 通用指针,可指向任意类型
4. 用 sizeof 查看类型大小
printf("char : %zu 字节\n", sizeof(char));
printf("short : %zu 字节\n", sizeof(short));
printf("int : %zu 字节\n", sizeof(int));
printf("long : %zu 字节\n", sizeof(long));
printf("long long : %zu 字节\n", sizeof(long long));
printf("float : %zu 字节\n", sizeof(float));
printf("double : %zu 字节\n", sizeof(double));
在典型 64 位系统上的输出:
char : 1 字节
short : 2 字节
int : 4 字节
long : 8 字节(Windows 上为 4 字节)
long long : 8 字节
float : 4 字节
double : 8 字节
5. 类型选择指南
面对一个具体需求时,按以下优先级选择类型:
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| 常规计数、索引、年龄 | int |
范围足够大,处理速度最快,是天然字长 |
| 明确非负的计数 | unsigned int |
语义清晰,范围大一倍 |
| 嵌入式/内存敏感的计数器 | unsigned char 或 unsigned short |
节省内存 |
| 科学计算、工程数值 | double |
精度高,现代 CPU 对 double 运算也有硬件加速 |
| 图形学/游戏中的颜色、坐标 | float |
精度足够且内存占用是 double 的一半 |
| ASCII 字符 | char |
1 字节,语义明确 |
| 文件大小、极大数值 | long long |
8 字节,范围足够 |
| 时间戳(秒) | time_t(通常是 long) |
使用标准库类型保证兼容性 |
决策流程图
需要存什么?
├── 整数
│ ├── 范围 ≤ 255 且非负 → unsigned char
│ ├── 范围 ≤ 65535 且非负 → unsigned short
│ ├── 范围 ≤ 21 亿 → int
│ ├── 明确非负、范围 ≤ 42 亿 → unsigned int
│ └── 超出以上 → long long
│
├── 小数
│ ├── 内存紧张、精度要求不高 → float
│ └── 默认选择 → double
│
├── 单个字符 → char
│
└── 无返回值/通用指针 → void
6. 类型转换(Type Casting)
隐式转换(自动)
当不同类型混合运算时,编译器自动将"窄"类型提升为"宽"类型:
int a = 10;
double b = 3.14;
double c = a + b; // a 被自动转换为 double,结果为 13.14
int d = a + b; // 13.14 被截断为 13(小数部分丢失)
显式转换(手动指定)
int x = 10, y = 3;
float result = (float)x / y; // 将 x 转为 float,得到 3.333333
// 如果不转换:10 / 3 = 3(整数除法截断)
类型提升规则
char / short → int → unsigned int → long → unsigned long
↘ float → double → long double
7. 总结
| 要点 | 说明 |
|---|---|
默认整数用 int |
范围足够,速度最快 |
默认小数用 double |
精度高,现代硬件支持好 |
float 有精度限制 |
不可直接 == 比较 |
sizeof 查看大小 |
不同平台结果可能不同 |
| 整数除法会截断 | 需要小数结果时先转换为浮点类型 |
| 无符号类型无负数 | 选择时确认语义是否需要负值 |
作者: Qing | 修改日期: 2026-07-18