2. 变量与数据类型:基本数据类型、变量声明与初始化、常量、类型转换
好,咱们今天聊聊变量和数据类型。这是C++里最基础的东西,但说实话,我见过不少老手在这上面翻车。我自己刚入行那会儿,也踩过几个坑,今天一并分享给你。
2.1 基本数据类型:int、float、char、bool
C++里内置了几种基本数据类型。说白了,就是用来存不同种类数据的“容器”。
| 类型 | 关键字 | 大小(通常) | 取值范围 |
|---|---|---|---|
| 整型 | int | 4字节 | -2^31 ~ 2^31-1 |
| 浮点型 | float | 4字节 | 约 ±3.4e-38 ~ ±3.4e38 |
| 字符型 | char | 1字节 | -128 ~ 127 或 0 ~ 255 |
| 布尔型 | bool | 1字节 | true 或 false |
嗯,这里要注意:int 的大小其实跟平台有关。在32位系统上是4字节,64位系统上通常也是4字节。我当年在嵌入式项目里吃过这个亏——以为 int 永远是4字节,结果在某个16位单片机上编译,它只有2字节,数据直接溢出了。
2.2 变量声明与初始化
变量声明,就是告诉编译器:“嘿,我要用一块内存,名字叫xxx。” 初始化呢,就是往这块内存里放个初始值。
我个人习惯,声明变量时尽量同时初始化。为什么?因为未初始化的变量里存的是“垃圾值”,你永远不知道它是什么。我曾经调试过一个bug,查了两天才发现是一个局部变量没初始化,导致程序行为随机崩溃。
#include <iostream>
using namespace std;
int main() {
// 声明并初始化
int age = 25;
float pi = 3.14159f;
char grade = 'A';
bool isReady = true;
// 也可以先声明,后赋值
int count;
count = 10;
// C++11 统一初始化语法(推荐)
int score{95};
float temperature{36.5f};
cout << "age: " << age << endl;
cout << "pi: " << pi << endl;
cout << "grade: " << grade << endl;
cout << "isReady: " << isReady << endl;
cout << "count: " << count << endl;
cout << "score: " << score << endl;
cout << "temperature: " << temperature << endl;
return 0;
}
{} 初始化有个好处——它能防止“窄化转换”。比如 int x{3.14}; 会编译报错,因为 double 转 int 可能丢失精度。而 int x = 3.14; 只会默默截断,容易埋坑。
2.3 常量
常量就是值不能变的变量。C++里有两种常用方式:const 和 constexpr。
- const:运行时常量,值在初始化后不能修改。
- constexpr:编译期常量,值在编译时就确定了。
#include <iostream>
using namespace std;
int main() {
const double PI = 3.1415926535;
constexpr int MAX_SIZE = 1024;
// PI = 3.14; // 错误!不能修改常量
// MAX_SIZE = 2048; // 错误!
cout << "PI: " << PI << endl;
cout << "MAX_SIZE: " << MAX_SIZE << endl;
return 0;
}
我个人建议:能用 constexpr 就用 constexpr。因为编译期求值能提升性能,而且更安全。我在做游戏引擎时,很多配置参数都用 constexpr,编译完就直接嵌入代码里了,运行时零开销。
const int x; 然后再赋值。编译器会直接报错。
2.4 类型转换
类型转换,就是把一种数据类型变成另一种。C++里分两种:隐式转换和显式转换。
隐式转换
编译器自动帮你转。比如 int 转 float,或者 char 转 int。但有时候会出问题——比如 float 转 int 会丢失小数部分。
int a = 10;
float b = a; // 隐式转换:int -> float,没问题
float c = 3.99;
int d = c; // 隐式转换:float -> int,d 变成 3,小数丢了
显式转换
你主动告诉编译器:“我就是要转,出问题我自己负责。” 常用方式有 C 风格和 C++ 风格。
#include <iostream>
using namespace std;
int main() {
double price = 19.99;
// C 风格转换(不推荐)
int priceInt1 = (int)price;
// C++ 风格转换(推荐)
int priceInt2 = static_cast<int>(price);
cout << "price: " << price << endl;
cout << "priceInt1: " << priceInt1 << endl;
cout << "priceInt2: " << priceInt2 << endl;
return 0;
}
static_cast 而不是 C 风格转换。因为 static_cast 在编译时做类型检查,能提前发现一些不合理的转换。我见过太多因为 C 风格转换导致的隐蔽 bug 了。
2.5 知识体系图
下面这张图帮你理清本章的核心脉络。变量和数据类型的关系,以及常量和类型转换的位置,一目了然。
2.6 避坑指南
- 未初始化变量:我曾经在写网络协议栈时,一个
int变量没初始化,结果在特定条件下它是个随机值,导致校验和算错,整整排查了两天。记住:声明时顺手初始化。 - 浮点数比较:别直接用
==比较两个float。因为浮点数有精度误差。用fabs(a - b) < 1e-6这种形式更靠谱。 - char 是有符号还是无符号? 这取决于编译器。如果你要存 0~255 的值,明确用
unsigned char,别指望char默认是无符号的。 - 类型转换别滥用:能用隐式转换就别用显式转换。但如果你必须转,用
static_cast而不是 C 风格。我见过有人用(int*)乱转指针,结果程序直接崩了。
好了,这一章的内容就这些。变量和数据类型是C++的基石,你把它搞扎实了,后面学指针、类、模板都会轻松很多。记住我上面说的那些坑,写代码时多留个心眼。