Ch 5: 变量和类型基础
- 理解 C++ 的静态类型系统
- 掌握变量声明和初始化
- 了解基本类型的大小和范围
- 学会使用
const和constexpr - 理解类型转换的规则
5.1 Python vs C++ 变量
Section titled “5.1 Python vs C++ 变量”5.1.1 Python 变量:动态绑定
Section titled “5.1.1 Python 变量:动态绑定”Python 中,变量是对象的引用,类型与对象绑定,而非变量绑定:
# Python - 变量是对象的引用x = 42 # x 引用整数对象 42x = "hello" # x 现在引用字符串对象 "hello"x = [1, 2, 3] # x 现在引用列表对象
# 类型跟随值,而非变量print(type(x)) # <class 'list'>这意味着:
- 变量可以在任何时候引用任何类型的对象
- 类型检查在运行时进行
- 灵活但容易出错
5.1.2 C++ 变量:静态绑定
Section titled “5.1.2 C++ 变量:静态绑定”C++ 中,变量在声明时就确定了类型,类型不可改变:
// C++ - 变量有固定类型int x = 42; // x 是 int,值为 42x = "hello"; // 编译错误!不能把字符串赋值给 intx = 3.14; // 编译警告!double 转 int 丢失精度
// 类型在编译时就确定std::string s = "hello"; // s 是 std::string// s = 42; // 编译错误!这意味着:
- 类型错误在编译时就被发现
- 编译器可以进行优化
- 更安全但灵活性较低
5.1.3 核心区别
Section titled “5.1.3 核心区别”# Python - 类型可以随时改变name = "Alice" # strname = 30 # int,现在 name 是 intname = [1, 2, 3] # list,现在 name 是 list// C++ - 类型一旦确定不可改变std::string name = "Alice"; // name 是 std::string// name = 30; // 编译错误!类型不匹配int age = 30; // age 是 int,age 和 name 是不同类型5.2 变量声明和初始化
Section titled “5.2 变量声明和初始化”5.2.1 四种初始化方式
Section titled “5.2.1 四种初始化方式”// 1. 直接初始化(拷贝初始化)int a = 42;
// 2. 大括号初始化(列表初始化,推荐)int b{42};int c = {42}; // = {} 等价于 {}
// 3. 默认初始化int d; // 未定义值!危险!
// 4. 值初始化int e{}; // 初始化为 05.2.2 大括号初始化的优势:防止窄化
Section titled “5.2.2 大括号初始化的优势:防止窄化”Python 不阻止精度丢失:
# Python - 允许隐式转换x = 3.14y = int(x) # y = 3,丢失精度但不报错C++ 大括号拒绝窄化转换:
// C++ - 编译时拒绝窄化转换int a = 3.14; // 编译警告:double 转 int 丢失精度int b{3.14}; // 编译错误!窄化转换被拒绝
// 显式允许窄化int c = static_cast<int>(3.14); // OK,显式转换
// 数字分隔符(C++14)int million = 1'000'000;long long big = 9'000'000'000'000LL;5.2.3 未初始化变量的危险
Section titled “5.2.3 未初始化变量的危险”// 危险!未初始化变量int x;std::cout << x << std::endl; // 未定义行为!
// 正确做法int x = 0; // 初始化为 0int x{}; // 值初始化为 0int x{42}; // 初始化为 425.2.4 auto 推导(C++11)
Section titled “5.2.4 auto 推导(C++11)”// auto 自动推导类型auto a = 42; // intauto b = 3.14; // doubleauto c = "hello"; // const char*auto d = std::string("hello"); // std::string
// 推导规则auto e = 1'000'000; // int(数字分隔符不改变类型)auto f = 1ULL; // unsigned long long5.3 基本类型
Section titled “5.3 基本类型”5.3.1 整数类型
Section titled “5.3.1 整数类型”| 类型 | 典型大小 | 范围 | Python 对应 |
|---|---|---|---|
bool | 1 byte | true/false | bool |
int | 4 bytes | -2³¹ ~ 2³¹-1 | int |
long | 4 或 8 bytes | 平台相关 | int |
long long | 8 bytes | -2⁶³ ~ 2⁶³-1 | int(无限制) |
unsigned int | 4 bytes | 0 ~ 2³²-1 | int(无负数) |
// 整数示例int population = 8'000'000'000; // 80 亿long long big_number = 9'000'000'000'000LL; // 9 万亿unsigned int natural = 42; // 非负整数
// 查看大小#include <climits>std::cout << INT_MAX << std::endl; // 2147483647std::cout << LLONG_MAX << std::endl; // 92233720368547758075.3.2 浮点类型
Section titled “5.3.2 浮点类型”| 类型 | 典型大小 | 精度 | Python 对应 |
|---|---|---|---|
float | 4 bytes | ~7 位十进制 | float |
double | 8 bytes | ~15 位十进制 | float(默认) |
long double | 16 bytes | 更高精度 | float(更高) |
// 浮点示例float f = 3.14f; // f 后缀表示 floatdouble d = 3.141592653; // doublelong double ld = 3.14L; // L 后缀
// 科学计数法double avogadro = 6.02e23;double planck = 6.626e-34;5.3.3 字符类型
Section titled “5.3.3 字符类型”| 类型 | 大小 | 说明 | Python 对应 |
|---|---|---|---|
char | 1 byte | ASCII 或 UTF-8 | str(单字符) |
char16_t | 2 bytes | UTF-16 | str |
char32_t | 4 bytes | UTF-32 | str |
wchar_t | 平台相关 | 宽字符 | str |
// 字符char grade = 'A';char digit = '7';
// 字符串std::string name = "Alice";
// Unicode(C++20 前用 u8/u/U 前缀)char8_t c8 = u8'中'; // C++20char16_t c16 = u'中'; // UTF-16char32_t c32 = U'中'; // UTF-325.3.4 sizeof 获取大小
Section titled “5.3.4 sizeof 获取大小”#include <iostream>
int main() { std::cout << "sizeof(int) = " << sizeof(int) << " bytes" << std::endl; std::cout << "sizeof(long long) = " << sizeof(long long) << " bytes" << std::endl; std::cout << "sizeof(double) = " << sizeof(double) << " bytes" << std::endl; std::cout << "sizeof(bool) = " << sizeof(bool) << " bytes" << std::endl; std::cout << "sizeof(char) = " << sizeof(char) << " bytes" << std::endl; std::cout << "sizeof(int*) = " << sizeof(int*) << " bytes" << std::endl;
// 与 Python 对比 // Python: sys.getsizeof(int) = 28 bytes(Python int 对象开销) // C++: sizeof(int) = 4 bytes(纯数据) return 0;}5.3.5 类型大小的影响
Section titled “5.3.5 类型大小的影响”// Python - 对象开销大x = 1_000_000 个整数# 内存 ≈ 28MB(每整数对象开销)
// C++ - 纯数据std::vector<int> v(1_000_000);# 内存 = 4MB(每 int 4 字节)5.4 常量
Section titled “5.4 常量”5.4.1 const 变量
Section titled “5.4.1 const 变量”# Python - 常量只是约定PI = 3.14159 # 仍可修改,但不该改PI = 3.14 # 不报错,但不好// C++ - 编译期强制const double PI = 3.14159;PI = 3.14; // 编译错误!常量不能修改5.4.2 constexpr 编译期常量
Section titled “5.4.2 constexpr 编译期常量”// const - 运行时常量const int get_size() { return 10; }int arr1[get_size()]; // 错误!const 不保证编译期求值
// constexpr - 编译期常量constexpr int get_size() { return 10; }int arr2[get_size()]; // OK!编译期就知道大小
// constexpr 函数要求(C++14)constexpr int factorial(int n) { int result = 1; for (int i = 2; i <= n; ++i) { result *= i; } return result;}
constexpr int f5 = factorial(5); // 编译期计算:1205.4.3 字面量
Section titled “5.4.3 字面量”// 整数字面量42 // int42L // long42LL // long long42u // unsigned42ULL // unsigned long long0xFF // 十六进制 (255)0b1010 // 二进制 (10)010 // 八进制 (8)
// 浮点字面量3.14 // double3.14f // float3.14L // long double1e10 // 科学计数法 (1.0 × 10¹⁰)
// 字符字面量'A' // char'\n' // 换行符'\t' // 制表符'\x41' // 十六进制 ASCII'A' // Unicode 16位'\U00000041' // Unicode 32位
// 字符串字面量"hello" // const char[6]u8"hello" // UTF-8u"hello" // UTF-16U"hello" // UTF-32R"(raw \n string)" // 原始字符串(不转义)5.4.4 constinit(C++20)
Section titled “5.4.4 constinit(C++20)”// constinit - 编译期初始化,用于静态存储期变量constinit int BUFFER_SIZE = 1024;constinit double GRAVITY = 9.8;
// consteval - 强制编译期求值(C++20)consteval int square(int x) { return x * x;}
int arr[square(5)]; // OK,编译期计算// square(get_runtime_value()); // 错误5.5 类型别名
Section titled “5.5 类型别名”5.5.1 using 别名
Section titled “5.5.1 using 别名”# Python - type hintsfrom typing import List, DictIntList = list[int] # Python 3.9+DictStrInt = Dict[str, int]// C++ - using 别名using IntList = std::vector<int>;using StringToInt = std::unordered_map<std::string, int>;using Callback = std::function<void(int)>;
IntList numbers = {1, 2, 3};StringToInt ages = {{"Alice", 30}, {"Bob", 25}};5.5.2 typedef(旧语法)
Section titled “5.5.2 typedef(旧语法)”// typedef 旧语法typedef std::vector<int> IntVector;typedef std::function<void(int)> Callback;
// using 更清晰(现代 C++ 推荐)using IntVector = std::vector<int>;using Callback = std::function<void(int)>;
// using 可以模板化(typedef 不能)template<typename T>using Vec = std::vector<T>;5.6 类型转换
Section titled “5.6 类型转换”5.6.1 隐式转换
Section titled “5.6.1 隐式转换”Python 会自动转换类型:
# Python - 自动类型提升result = 3 + 3.14 # 3 转为 3.0,结果是 6.14result = 3 / 2 # 地板除?浮点除?根据 Python 版本和行为C++ 也有隐式转换:
// C++ - 隐式转换(可能丢失精度)int i = 3;double d = 3.14;double result = i + d; // i 转 double: 6.14
int x = d; // 警告:double 转 int: 3(丢失精度)5.6.2 显式转换
Section titled “5.6.2 显式转换”C++ 有四种类型转换操作符:
// 1. static_cast - 编译期可检查的转换double d = 3.14;int i = static_cast<int>(d); // 3,显式转换
// 2. const_cast - 移除 const 属性const int* pc = &i;int* p = const_cast<int*>(pc); // 移除 const
// 3. reinterpret_cast - 位层面重新解释int* ip = &i;char* cp = reinterpret_cast<char*>(ip); // 把 int* 当 char* 看
// 4. dynamic_cast - 运行时多态转换class Base { virtual void foo() {} };class Derived : public Base {};Base* b = new Derived;Derived* d = dynamic_cast<Derived*>(b); // 安全检查5.6.3 转换的安全性
Section titled “5.6.3 转换的安全性”# Python - 运行时检查data = {"a": 1}value = data.get("b", "default") # 安全,不存在返回默认值// C++ - 默认不检查std::unordered_map<std::string, int> data = {{"a", 1}};int value = data.at("a"); // 安全,抛异常int value2 = data["b"]; // 不安全!插入默认值 05.7 完整示例
Section titled “5.7 完整示例”#include <iostream>#include <string>#include <vector>#include <unordered_map>#include <iomanip>
int main() { // ===== 基本类型 ===== int age = 25; double height = 1.75; char grade = 'A'; bool is_student = true; std::string name = "Alice";
// ===== const 和 constexpr ===== const int MAX_RETRY = 3; constexpr double PI = 3.14159265358979; constexpr int ARRAY_SIZE = 5;
// ===== 数组 ===== int scores[ARRAY_SIZE] = {95, 87, 92, 78, 88};
// ===== 类型别名 ===== using ScoreMap = std::unordered_map<std::string, int>; ScoreMap grades = { {"Alice", 95}, {"Bob", 88}, {"Charlie", 92} };
// ===== 输出 ===== std::cout << "===== 基本信息 =====" << std::endl; std::cout << "姓名: " << name << std::endl; std::cout << "年龄: " << age << std::endl; std::cout << "身高: " << height << "m" << std::endl; std::cout << "等级: " << grade << std::endl; std::cout << "学生: " << std::boolalpha << is_student << std::endl; std::cout << "π: " << std::setprecision(6) << PI << std::endl;
// ===== 数组输出 ===== std::cout << "\n===== 成绩 =====" << std::endl; int sum = 0; for (int i = 0; i < ARRAY_SIZE; ++i) { std::cout << "第" << (i + 1) << "门: " << scores[i] << std::endl; sum += scores[i]; } std::cout << "平均分: " << static_cast<double>(sum) / ARRAY_SIZE << std::endl;
// ===== 字典遍历 ===== std::cout << "\n===== 等级 =====" << std::endl; for (const auto& [student, score] : grades) { std::cout << student << ": " << score << std::endl; }
// ===== 类型大小 ===== std::cout << "\n===== 类型大小 =====" << std::endl; std::cout << "sizeof(int): " << sizeof(int) << std::endl; std::cout << "sizeof(double): " << sizeof(double) << std::endl; std::cout << "sizeof(bool): " << sizeof(bool) << std::endl; std::cout << "sizeof(std::string): " << sizeof(std::string) << std::endl;
return 0;}- C++ 是静态类型语言,类型在编译时确定
- 使用
{}初始化防止窄化转换 - 基本类型:int、double、char、bool
const用于运行时常量,constexpr用于编译时常量- 类型转换:
static_cast<T>是首选显式转换方式 - 变量未初始化是危险的行为
下章预告:ch06 学习 auto 关键字和 C++ 的类型推导机制。