Skip to content

Ch 5: 变量和类型基础

  • 理解 C++ 的静态类型系统
  • 掌握变量声明和初始化
  • 了解基本类型的大小和范围
  • 学会使用 const 和 constexpr
  • 理解类型转换的规则

Python 中,变量是对象的引用,类型与对象绑定,而非变量绑定:

# Python - 变量是对象的引用
x = 42 # x 引用整数对象 42
x = "hello" # x 现在引用字符串对象 "hello"
x = [1, 2, 3] # x 现在引用列表对象
# 类型跟随值,而非变量
print(type(x)) # <class 'list'>

这意味着:

  • 变量可以在任何时候引用任何类型的对象
  • 类型检查在运行时进行
  • 灵活但容易出错

C++ 中,变量在声明时就确定了类型,类型不可改变:

// C++ - 变量有固定类型
int x = 42; // x 是 int,值为 42
x = "hello"; // 编译错误!不能把字符串赋值给 int
x = 3.14; // 编译警告!double 转 int 丢失精度
// 类型在编译时就确定
std::string s = "hello"; // s 是 std::string
// s = 42; // 编译错误!

这意味着:

  • 类型错误在编译时就被发现
  • 编译器可以进行优化
  • 更安全但灵活性较低
# Python - 类型可以随时改变
name = "Alice" # str
name = 30 # int,现在 name 是 int
name = [1, 2, 3] # list,现在 name 是 list
// C++ - 类型一旦确定不可改变
std::string name = "Alice"; // name 是 std::string
// name = 30; // 编译错误!类型不匹配
int age = 30; // age 是 int,age 和 name 是不同类型
// 1. 直接初始化(拷贝初始化)
int a = 42;
// 2. 大括号初始化(列表初始化,推荐)
int b{42};
int c = {42}; // = {} 等价于 {}
// 3. 默认初始化
int d; // 未定义值!危险!
// 4. 值初始化
int e{}; // 初始化为 0

5.2.2 大括号初始化的优势:防止窄化

Section titled “5.2.2 大括号初始化的优势:防止窄化”

Python 不阻止精度丢失:

# Python - 允许隐式转换
x = 3.14
y = 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;
// 危险!未初始化变量
int x;
std::cout << x << std::endl; // 未定义行为!
// 正确做法
int x = 0; // 初始化为 0
int x{}; // 值初始化为 0
int x{42}; // 初始化为 42
// auto 自动推导类型
auto a = 42; // int
auto b = 3.14; // double
auto c = "hello"; // const char*
auto d = std::string("hello"); // std::string
// 推导规则
auto e = 1'000'000; // int(数字分隔符不改变类型)
auto f = 1ULL; // unsigned long long
类型典型大小范围Python 对应
bool1 bytetrue/falsebool
int4 bytes-2³¹ ~ 2³¹-1int
long4 或 8 bytes平台相关int
long long8 bytes-2⁶³ ~ 2⁶³-1int(无限制)
unsigned int4 bytes0 ~ 2³²-1int(无负数)
// 整数示例
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; // 2147483647
std::cout << LLONG_MAX << std::endl; // 9223372036854775807
类型典型大小精度Python 对应
float4 bytes~7 位十进制float
double8 bytes~15 位十进制float(默认)
long double16 bytes更高精度float(更高)
// 浮点示例
float f = 3.14f; // f 后缀表示 float
double d = 3.141592653; // double
long double ld = 3.14L; // L 后缀
// 科学计数法
double avogadro = 6.02e23;
double planck = 6.626e-34;
类型大小说明Python 对应
char1 byteASCII 或 UTF-8str(单字符)
char16_t2 bytesUTF-16str
char32_t4 bytesUTF-32str
wchar_t平台相关宽字符str
// 字符
char grade = 'A';
char digit = '7';
// 字符串
std::string name = "Alice";
// Unicode(C++20 前用 u8/u/U 前缀)
char8_t c8 = u8'中'; // C++20
char16_t c16 = u'中'; // UTF-16
char32_t c32 = U'中'; // UTF-32
#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;
}
// Python - 对象开销大
x = 1_000_000 个整数
# 内存 ≈ 28MB(每整数对象开销)
// C++ - 纯数据
std::vector<int> v(1_000_000);
# 内存 = 4MB(每 int 4 字节)
# Python - 常量只是约定
PI = 3.14159 # 仍可修改,但不该改
PI = 3.14 # 不报错,但不好
// C++ - 编译期强制
const double PI = 3.14159;
PI = 3.14; // 编译错误!常量不能修改
// 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); // 编译期计算:120
// 整数字面量
42 // int
42L // long
42LL // long long
42u // unsigned
42ULL // unsigned long long
0xFF // 十六进制 (255)
0b1010 // 二进制 (10)
010 // 八进制 (8)
// 浮点字面量
3.14 // double
3.14f // float
3.14L // long double
1e10 // 科学计数法 (1.0 × 10¹⁰)
// 字符字面量
'A' // char
'\n' // 换行符
'\t' // 制表符
'\x41' // 十六进制 ASCII
'A' // Unicode 16位
'\U00000041' // Unicode 32位
// 字符串字面量
"hello" // const char[6]
u8"hello" // UTF-8
u"hello" // UTF-16
U"hello" // UTF-32
R"(raw \n string)" // 原始字符串(不转义)
// 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()); // 错误
# Python - type hints
from typing import List, Dict
IntList = 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}};
// 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>;

Python 会自动转换类型:

# Python - 自动类型提升
result = 3 + 3.14 # 3 转为 3.0,结果是 6.14
result = 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(丢失精度)

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); // 安全检查
# 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"]; // 不安全!插入默认值 0
variables.cpp
#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++ 的类型推导机制。