1. 什么是数据类型?
数据类型告诉编译器:要存储什么种类的数据 以及需要分配多少内存空间 。C++ 是一种静态类型语言,变量的类型在编译时就必须确定。
C++ 内置的基本数据类型(也叫算术类型)分为两类:
- 整型:包括整数、字符、布尔值
- 浮点型:小数
2. 整型(Integer Types)
整型用于存储整数(没有小数部分的数)。
2.1 整型分类与大小
| 类型 | 关键字 | 典型大小(字节) | 取值范围(以32位系统为例) |
|---|---|---|---|
| 短整型 | short (或 short int) |
2 | -32,768 ~ 32,767 |
| 整型 | int |
4 | -2,147,483,648 ~ 2,147,483,647 |
| 长整型 | long (或 long int) |
4(Windows)/ 8(Linux) | 见上或更大 |
| 长长整型 | long long (C++11) |
8 | -9.22×10¹⁸ ~ 9.22×10¹⁸ |
注意:
int的大小取决于编译器和平台(通常为4字节)。C++ 标准只规定了最小大小:short≥ 2字节,int≥ 2字节,long≥ 4字节,long long≥ 8字节。
2.2 有符号与无符号
- 有符号 (默认):可以表示负数、零、正数。如
int。 - 无符号 :只能表示非负数,范围更大。用
unsigned前缀。
cpp
unsigned int a = 4000000000; // 正数范围扩大
signed int b = -100; // signed 可省略
| 类型 | 取值范围(4字节) |
|---|---|
int |
-2,147,483,648 ~ 2,147,483,647 |
unsigned int |
0 ~ 4,294,967,295 |
2.3 代码示例:查看大小和溢出
cpp
#include <iostream>
#include <climits> // 包含整型极限常量
using namespace std;
int main() {
cout << "int 大小:" << sizeof(int) << " 字节" << endl;
cout << "short 大小:" << sizeof(short) << " 字节" << endl;
cout << "long 大小:" << sizeof(long) << " 字节" << endl;
cout << "long long 大小:" << sizeof(long long) << " 字节" << endl;
// 查看最大最小值(climits)
cout << "int 最大值:" << INT_MAX << endl;
cout << "int 最小值:" << INT_MIN << endl;
cout << "unsigned int 最大值:" << UINT_MAX << endl;
// 演示溢出
unsigned int u = 0;
u = u - 1; // 无符号整数下溢,变成最大值
cout << "0 - 1 = " << u << " (无符号溢出)" << endl;
int i = 2147483647; // int 最大值
i = i + 1; // 溢出,结果是未定义的(通常是负数)
cout << "2147483647 + 1 = " << i << " (有符号溢出)" << endl;
return 0;
}
可能的输出(32位环境):
int 大小:4 字节
short 大小:2 字节
long 大小:4 字节
long long 大小:8 字节
int 最大值:2147483647
int 最小值:-2147483648
unsigned int 最大值:4294967295
0 - 1 = 4294967295 (无符号溢出)
2147483647 + 1 = -2147483648 (有符号溢出)
⚠️ 有符号整型溢出是未定义行为,程序可能崩溃或产生奇怪结果。无符号溢出则是按模运算(回绕)。
3. 浮点型(Floating-Point Types)
用于存储实数(带小数点的数),遵循 IEEE 754 标准。
| 类型 | 关键字 | 典型大小(字节) | 精度(有效十进制位数) | 取值范围(约) |
|---|---|---|---|---|
| 单精度 | float |
4 | 6~7 位 | ±3.4×10³⁸ |
| 双精度 | double |
8 | 15~16 位 | ±1.7×10³⁰⁸ |
| 扩展精度 | long double |
8/12/16 依平台 | 更高 | 更大 |
cpp
#include <iostream>
#include <iomanip>
#include <cfloat> // 浮点极限常量
using namespace std;
int main() {
float f = 3.1415926535f; // f 后缀表示 float
double d = 3.1415926535;
long double ld = 3.1415926535L;
cout << "float 大小:" << sizeof(float) << " 字节" << endl;
cout << "double 大小:" << sizeof(double) << " 字节" << endl;
cout << "long double 大小:" << sizeof(long double) << " 字节" << endl;
// 精度演示:float 只能保留约7位有效数字
cout << fixed << setprecision(10);
cout << "float 值 : " << f << endl; // 可能输出 3.1415927410
cout << "double 值 : " << d << endl; // 输出 3.1415926535
// 浮点比较陷阱
float f1 = 0.1f;
float f2 = 0.2f;
float f3 = f1 + f2;
if (f3 == 0.3f) {
cout << "相等" << endl;
} else {
cout << "不相等,实际值:" << setprecision(20) << f3 << endl;
}
return 0;
}
🔍 浮点精度问题 :0.1 在二进制中是无限循环小数,无法精确表示。因此浮点数比较应该使用容差(如
fabs(a-b) < 1e-6)。
4. 字符型(Character Types)
字符类型用于存储单个字符(本质是小整数,存储 ASCII 或 Unicode 码点)。
| 类型 | 说明 | 典型大小 | 编码 |
|---|---|---|---|
char |
单字节字符 | 1 字节 | ASCII(通常) |
signed char |
有符号字符 | 1 字节 | -128~127 |
unsigned char |
无符号字符 | 1 字节 | 0~255 |
wchar_t |
宽字符(用于国际化) | 2 或 4 字节 | Unicode (UTF-16/32) |
char16_t (C++11) |
UTF-16 字符 | 2 字节 | UTF-16 |
char32_t (C++11) |
UTF-32 字符 | 4 字节 | UTF-32 |
cpp
#include <iostream>
using namespace std;
int main() {
char ch = 'A'; // 字符字面量用单引号
char ascii_val = 65; // 等价于 'A'
cout << ch << " 的 ASCII 码:" << (int)ch << endl;
// 字符参与算术运算(按整数)
char next = ch + 1;
cout << "A 的后一个字符:" << next << endl; // 'B'
// 转义字符
cout << "换行符:\n,制表符:\t,反斜杠:\\,单引号:\'" << endl;
// 宽字符示例
wchar_t wch = L'中'; // L 前缀表示宽字符
wcout << L"宽字符:" << wch << endl;
return 0;
}
常见转义字符 :\n(换行)、\t(制表)、\\(反斜杠)、\'(单引号)、\"(双引号)、\0(空字符)。
5. 布尔型(Boolean Type)
bool 类型用于表示逻辑真或假。它的大小通常为 1 字节(C++ 标准未强制,但大多数编译器实现为1字节)。
cpp
#include <iostream>
using namespace std;
int main() {
bool is_ready = true;
bool is_empty = false;
cout << "bool 大小:" << sizeof(bool) << " 字节" << endl;
cout << "true 的整数值:" << (int)true << endl; // 1
cout << "false 的整数值:" << (int)false << endl; // 0
// 任何非零值转换为 true,零转换为 false
bool b1 = 100; // true
bool b2 = 0; // false
bool b3 = -5.5; // true(非零)
// 逻辑运算
bool result = (is_ready && !is_empty); // true
cout << boolalpha; // 让 bool 输出 true/false 而不是 1/0
cout << "result = " << result << endl;
return 0;
}
6. 内存模型讲解(浅显易懂)
所有变量在内存中占据连续的字节。不同类型占用的字节数不同,且字节内部的二进制表示也不同。
6.1 整型的内存表示(以 int 为例)
假设有一个 int a = 42;(4字节,小端序,32位系统):
内存地址(低 → 高):
地址 1000: 00101010 (0x2A) ← 最低有效字节
地址 1001: 00000000 (0x00)
地址 1002: 00000000 (0x00)
地址 1003: 00000000 (0x00)
- 42 的二进制是
...00101010,低字节存储在第 1000 号地址(小端序)。 - 有符号整数用补码表示负数。
6.2 浮点型的内存表示(float)
float 遵循 IEEE 754 单精度格式:1位符号位 + 8位指数 + 23位尾数。
例如 float f = 3.14f; 在内存中(近似):
符号(1) 指数(8) 尾数(23)
0 10000000 10010001111010111000011
这解释了为什么浮点数不能精确表示某些十进制小数。
6.3 字符型的内存表示
字符变量存储的是其 ASCII 码值(一个整数)。例如 char ch = 'A'; 在内存中就是一个字节:
地址 2000: 01000001 (十进制 65)
6.4 布尔型的内存表示
bool 变量虽然只表示 0 或 1,但仍占用 1 字节(为了可寻址性)。true 通常存储为 1,false 为 0。
6.5 内存布局示意图
内存区域(低地址 → 高地址)
┌────────────────────────────────────────────────────────────┐
│ char c = 'A' │ int i = 42 │ float f = 3.14 │ ... │
│ [65] (1字节) │ [2A 00 00 00] │ [C3 F5 48 40] │ │
└────────────────────────────────────────────────────────────┘
注意:编译器可能会在变量之间插入填充字节(对齐),以提高访问速度。因此结构体的大小不一定等于成员大小之和。
7. 常见错误与避坑
| 错误示例 | 问题 | 正确做法 |
|---|---|---|
float f = 3.14; |
字面量 3.14 是 double 类型,会隐式转换 |
用 3.14f 后缀 |
int a = 3.14; |
截断小数部分,可能丢失信息 | 用 double 或显式转换 |
char c = "A"; |
双引号是字符串,不能用单引号 | 'A' |
bool b = (0.0 == 0); |
浮点与整数比较,但 0.0 精确 | 一般浮点用容差比较 |
unsigned int u = -1; |
无符号变量赋负数,变成大正数 | 除非有意利用回绕,否则避免 |
8. 练习题
题目:编写一个 C++ 程序,完成以下任务:
- 使用
sizeof运算符输出当前平台上char,short,int,long,long long,float,double,long double,bool所占的字节数。- 定义一个
unsigned short变量u16并赋值为 65535(该类型的最大值),然后对其加 1,输出结果,解释发生了什么。- 定义两个
float变量a = 1.0 / 3.0和b = 1.0 / 3.0,计算a * 3.0和b * 3.0,分别输出结果,观察它们是否精确等于 1.0。- 定义一个
char变量并赋值为'Z',输出它的 ASCII 码(整数值)以及它的下一个字符('Z' + 1)。期望输出格式示例:
char: 1 字节 short: 2 字节 ... 65535 + 1 = 0 (无符号溢出回绕) 1.0/3.0 * 3.0 = 1 (但实际浮点值可能为 0.999999...) 'Z' 的 ASCII 码: 90, 下一个字符: '['
上期小练习答案
cpp
#include <iostream>
using namespace std;
constexpr int kStudentCount = 5; // 编译时常量
const double kPi = 3.14159; // 运行时只读常量
int Increment() {
static int counter = 0; // 静态局部变量,只初始化一次,生命周期贯穿整个程序
return ++counter;
}
int main() {
// 3. 未初始化的局部变量
int uninit_val;
cout << "未初始化的值:" << uninit_val << endl; // 输出垃圾值
// 4. 测试静态计数器
cout << "第1次调用:" << Increment() << endl;
cout << "第2次调用:" << Increment() << endl;
cout << "第3次调用:" << Increment() << endl;
// 5. 尝试修改常量(取消注释会编译错误)
// kPi = 3.14; // error: assignment of read-only variable 'kPi'
// 附加挑战
const int size_const = 10; // 运行时初始化,但值已知,在支持 VLA 的编译器可能允许
constexpr int size_constexpr = 10;
int arr1[size_constexpr]; // 标准 C++ 合法
int arr2[size_const]; // 标准 C++ 不合法(gcc 扩展允许)
cout << "kStudentCount = " << kStudentCount << endl;
cout << "kPi = " << kPi << endl;
return 0;
}
总结:C++ 的基本数据类型包括整型、浮点型、字符型和布尔型,每种类型有固定或平台相关的大小和取值范围。理解它们在内存中的表示(二进制、字节序、IEEE 754)有助于写出高效且无 bug 的代码。下一篇文章我们将学习类型别名与类型推导,让代码更简洁安全。