数据变量进阶
内容简介
本篇补充类型转换、整数/浮点运算细节、size_t 和固定宽度整数类型。C/C++ 会在表达式中自动进行类型提升,也允许程序员手动强制转换。
这里面向 CandCPP 主线,讲到能避开常见坑为止。C 语言规则的完整细节,包括整型提升、通常算术转换、有符号/无符号混用和指针强转边界,放到 C语言类型转换规则。
这部分看起来像“小细节”,但实际很容易造成 bug:
8 / 5为什么是1。int value = 23.9;为什么小数丢失。unsigned int a = 0; a - 1为什么变成很大的数。size_t为什么经常和int比较时有警告。int32_t和int有什么区别。
用法
自动类型转换
整型提升
小于 int 的整数类型参与运算时,通常会先提升为 int 再计算。
char a = 1;
short b = 2;
int c = a + b; // a 和 b 会先提升为 int 再计算这就是为什么 char 参与算术运算后,结果通常不是 char。
整数除法和浮点除法
std::cout << 8 / 5 << std::endl; // 1
std::cout << 8.0 / 5 << std::endl; // 1.68 / 5 两边都是整数,所以执行整数除法,小数部分直接丢弃。
如果需要小数结果,至少让一个操作数是浮点数:
double result = 8.0 / 5;赋值时的窄化转换
double price = 23.9;
int value = price; // value 是 23,小数部分被截断从范围更大或精度更高的类型转到范围更小或精度更低的类型时,可能发生数据丢失。
有符号和无符号混用
int a = -1;
unsigned int b = 1;
std::cout << (a < b) << std::endl; // 结果可能不符合直觉有符号整数和无符号整数混合运算时,编译器可能把有符号数转换成无符号数。负数转换成无符号数后会变成很大的值。
强制类型转换
C 风格强制转换:
double result = (double)8 / 5;C++ 推荐写法:
double result = static_cast<double>(8) / 5;static_cast 比 C 风格转换更清楚,也更容易被代码审查发现。
size_t
size_t 是一种无符号整数类型,通常用来表示大小、长度、下标范围和内存字节数。
常见来源:
std::string text = "hello";
std::size_t len = text.size();
std::vector<int> nums = {1, 2, 3};
std::size_t count = nums.size();常见用途:
for (std::size_t i = 0; i < nums.size(); ++i) {
std::cout << nums[i] << std::endl;
}需要注意:size_t 是无符号类型,不适合直接表示可能为负的值。
std::size_t n = 0;
std::cout << n - 1 << std::endl; // 下溢,结果变成很大的数size_t 和 int 比较时也容易出现警告:
int i = -1;
std::vector<int> nums = {1, 2, 3};
if (i < nums.size()) {
// 容易产生有符号/无符号比较问题
}更稳妥的做法是先确认变量含义:
- 表示数量、长度、内存大小:用
std::size_t。 - 表示可能为负的差值、偏移、业务数值:用有符号整数。
固定宽度整数:int8_t、int16_t、int32_t、int64_t
普通 int 的大小通常是 32 位,但 C/C++ 标准并不把所有平台细节都写死。固定宽度整数用于明确指定整数位数。
头文件:
#include <cstdint>常见类型:
| 类型 | 含义 | 常见范围 |
|---|---|---|
int8_t | 8 位有符号整数 | -128 到 127 |
uint8_t | 8 位无符号整数 | 0 到 255 |
int16_t | 16 位有符号整数 | -32768 到 32767 |
uint16_t | 16 位无符号整数 | 0 到 65535 |
int32_t | 32 位有符号整数 | 约 -21 亿到 21 亿 |
uint32_t | 32 位无符号整数 | 0 到约 42 亿 |
int64_t | 64 位有符号整数 | 很大 |
uint64_t | 64 位无符号整数 | 非常大 |
示例:
#include <cstdint>
#include <iostream>
int main()
{
std::int32_t user_id = 1001;
std::uint64_t file_size = 1024;
std::cout << user_id << std::endl;
std::cout << file_size << std::endl;
return 0;
}适合使用固定宽度整数的场景:
- 网络协议字段。
- 二进制文件格式。
- 嵌入式寄存器。
- 跨平台序列化。
- 明确要求位宽的数据结构。
普通业务代码如果没有明确位宽要求,int、long long、std::size_t 往往更自然。
最佳代码实践
- C++ 中优先使用
static_cast<T>(value),比 C 风格强转更明确。 - 涉及有符号和无符号整数混合运算时,先明确类型。
- 需要小数结果时,把至少一个操作数转成浮点类型。
- 不要忽略编译器关于类型转换、溢出、精度丢失的警告。
- 表示容器大小、字符串长度、内存大小时使用
std::size_t。 - 对二进制协议、文件格式、嵌入式寄存器使用
std::int32_t、std::uint32_t等固定宽度整数。
错误用法
int value = 23.9; // 小数部分被截断,value 是 23unsigned int a = 0;
std::cout << a - 1 << std::endl; // 无符号下溢,结果会变成很大的数double result = static_cast<double>(8 / 5); // 先做整数除法,结果仍然是 1.0for (int i = 0; i < nums.size(); ++i) {
// 错误倾向:int 和 size_t 混合比较,可能有警告
}std::size_t index = -1; // 错误倾向:-1 转成很大的无符号数std::int8_t value = 200; // 超出 int8_t 常见范围注意事项
- 整型提升大致遵循“小类型先提升到 int,再参与运算”。
- 浮点数有精度误差,不适合直接表示金额等要求精确的小数。
- 强制类型转换能绕过部分类型检查,使用前要明确转换是否真的合理。
std::size_t来自<cstddef>,很多标准库头文件也会间接提供它;写 C++ 时推荐使用std::size_t。- 固定宽度整数来自
<cstdint>,C 语言中对应<stdint.h>。 int8_t和uint8_t在很多实现中本质接近字符类型,直接输出时可能按字符显示;需要数值显示时可转换成int。- C 语言细节继续看 C语言类型转换规则;C++ 转换写法继续看 cpp自动类型转换。
学习路径
- 上一节:CandCppconst
- 当前阶段:CandCPP基础
- 下一节:CandCpp枚举