乐途乐途
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
主页
  • 计算机基础

    • TCP/IP
    • Linux
    • HTTP
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C
    • C++
    • Java SE
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • Maven 入门
    • Maven 进阶
    • MyBatis
    • Spring
    • Spring MVC
  • Java

    • Spring Boot
    • Spring Cloud
    • Spring Cloud Alibaba
    • Spring Security
    • Spring AI
    • Spring Batch
    • Kafka
    • Java 设计模式
  • 缓存

    • Redis
  • 搜索引擎

    • Elasticsearch
  • 分布式协调

    • ZooKeeper
联系
阿里云
  • C++ 学习路径
  • 第1章 C++概述与开发环境

    • C++ 语言概述与编译模型
    • 第一个 C++ 程序与规范
    • 开发环境配置
    • 预处理指令详解
  • 第2章 基本语法与类型系统

    • 变量与基本类型
    • 类型转换
    • 枚举类型
    • 引用与指针
    • 数组与 stdarray
    • 字符串与原始字符串字面量
    • const 与 constexpr
    • nullptr 与空指针
    • 类型推导 auto 与 decltype
    • 基于范围的 for 循环
    • 值类别全面解析
    • char16_t 与 char32_t
    • static_assert 编译期断言
  • 第3章 函数与重载

    • 函数声明与定义
    • 函数重载
    • 默认参数与内联函数
    • Lambda 表达式
    • 函数对象与 stdfunction
    • 后置返回类型与 noexcept
  • 第4章 类与对象

    • 类的基本定义
    • 构造函数与析构函数
    • 拷贝控制
    • 移动构造函数与移动赋值
    • 列表初始化与类内初始化器
    • 静态成员与嵌套类
    • 友元
    • =default 与 =delete
  • 第5章 继承与多态

    • 继承基础
    • 虚函数与多态
    • 虚函数表与动态绑定原理
    • 虚析构函数
    • 抽象类与纯虚函数
    • 多重继承与虚继承
    • 继承构造函数
  • 第6章 运算符重载

    • 运算符重载基础
    • 算术与关系运算符重载
    • 赋值与移动运算符重载
    • 特殊运算符重载
  • 第7章 模板与泛型编程

    • 函数模板
    • 类模板
    • 模板特化与偏特化
    • 可变参数模板
    • 别名模板与模板模板参数
    • SFINAE 与类型萃取
    • 依赖名与 typename/template 关键字
  • 第8章 异常处理

    • 异常处理机制
    • noexcept 与异常安全
    • RAII 与异常安全实践
  • 第9章 内存管理与智能指针

    • 动态内存与内存分区
    • RAII 惯用法
    • unique_ptr
    • shared_ptr 与 weak_ptr
    • 内存管理最佳实践
  • 第10章 右值引用与移动语义

    • 右值引用与值类别深度解析
    • std::move 原理与使用
    • 完美转发与 std::forward
    • 移动语义性能对比与最佳实践
  • 第11章 STL容器

    • vector 深度剖析
    • deque 内部机制
    • list 与 forward_list
    • map 与 set 深度解析
    • unordered 容器与哈希原理
    • array 与 tuple
    • 容器适配器
    • 容器选择全景指南
  • 第12章 STL算法与迭代器

    • 迭代器体系全解
    • 非变异算法
    • 变异算法
    • 排序与二分算法
    • Lambda 与算法组合
    • std::random 随机数库
    • 自定义迭代器开发
  • 第13章 IO流与文件

    • 标准 IO 流
    • 格式化输出控制
    • 文件流操作
    • 字符串流
    • std::regex 正则表达式
  • 第14章 并发与多线程

    • thread 基础与线程管理
    • mutex 与 lock_guard
    • unique_lock 与 condition_variable
    • thread_local 线程局部存储
    • atomic 与内存序
    • future 与 async 异步编程
    • std::chrono 时间库
  • 第15章 现代C++新特性

    • 从 C++11 到 C++20 演进路线
    • C++14 关键新特性
    • C++17 关键新特性
    • C++20 核心特性速览
  • 第16章 面试考点与最佳实践

    • C++ 综合最佳实践清单
    • 高频面试题精讲
    • 多线程面试题与实战
    • 内存管理常见陷阱与排查
  • 附录

    • C++ 核心知识点
    • C++ 专业术语

变量与基本类型

定义与作用

变量是程序中数据的最小容器。C++ 的类型系统是静态强类型——每个变量的类型在编译期确定,且不可改变。基本类型(fundamental types)是 C++ 类型系统的基石,理解它们的存储大小、取值范围和运算规则,是编写正确、高效程序的先决条件。

核心原理

基本类型全景

类型大小与取值范围

类型最小宽度典型大小(64 位系统)取值范围
bool未指定1 字节true / false
char8 位1 字节-128 ~ 127 或 0 ~ 255
short16 位2 字节-32768 ~ 32767
int16 位4 字节约 ±21 亿
long32 位8 字节(Linux)/4 字节(Windows)平台相关
long long64 位8 字节约 ±9×10¹⁸
floatIEEE 754 单精度4 字节约 7 位有效数字
doubleIEEE 754 双精度8 字节约 15 位有效数字
long double≥ double8/16 字节平台相关

使用 <cstdint> 中的 int32_t、uint64_t 等固定宽度类型,可以消除平台差异。

内存布局示意

多字节类型的字节序分为大端序(高位在前)和小端序(低位在前),x86/x64 架构使用小端序。

初始化方式

C++ 提供多种变量初始化语法,C++11 统一了初始化语法:

int a = 10;        // 拷贝初始化
int b(10);         // 直接初始化(C++98)
int c{10};         // 列表初始化(C++11,推荐)
int d = {10};      // 拷贝列表初始化
int e{};           // 值初始化 → 0

列表初始化的优势:禁止窄化转换(narrowing),在编译期捕获精度丢失。

int valid{42};       // 正确
int invalid{3.14};   // 错误:窄化转换(double → int 丢失信息)

完整示例

示例一:学生信息卡片——基本类型实践

场景说明:创建一个学生信息结构,使用所有基本类型存储学号、年龄、成绩、是否在读等字段,并格式化输出。

#include <iostream>
#include <string>
#include <iomanip>

int main() {
    // 整型:学号、年龄、班级人数
    long long student_id{20240101001LL};
    short age{20};
    int class_size{45};

    // 浮点:GPA、学费
    float gpa{3.85f};
    double tuition{58000.50};

    // 字符与布尔
    char grade_level{'A'};
    bool is_enrolled{true};
    bool has_scholarship{false};

    // 固定宽度类型(推荐用于跨平台)
    uint32_t enrollment_year{2024};

    std::cout << "========== 学生信息卡片 ==========" << std::endl;
    std::cout << std::left << std::setw(18) << "学号:"
              << student_id << std::endl;
    std::cout << std::setw(18) << "年龄:" << age << std::endl;
    std::cout << std::setw(18) << "年级等级:" << grade_level << std::endl;
    std::cout << std::setw(18) << "入学年份:" << enrollment_year << std::endl;

    std::cout << std::fixed << std::setprecision(2);
    std::cout << std::setw(18) << "GPA:" << gpa << std::endl;
    std::cout << std::setw(18) << "学费:" << tuition << std::endl;

    std::cout << std::boolalpha;  // 输出 true/false 而非 1/0
    std::cout << std::setw(18) << "在读状态:" << is_enrolled << std::endl;
    std::cout << std::setw(18) << "奖学金:" << has_scholarship << std::endl;

    std::cout << std::setw(18) << "班级人数:" << class_size << std::endl;

    // 类型大小展示
    std::cout << "\n===== 类型大小 =====" << std::endl;
    std::cout << "short:         " << sizeof(short) << " 字节" << std::endl;
    std::cout << "int:           " << sizeof(int) << " 字节" << std::endl;
    std::cout << "long long:     " << sizeof(long long) << " 字节" << std::endl;
    std::cout << "float:         " << sizeof(float) << " 字节" << std::endl;
    std::cout << "double:        " << sizeof(double) << " 字节" << std::endl;
    std::cout << "bool:          " << sizeof(bool) << " 字节" << std::endl;

    return 0;
}

预期输出(64 位系统):

========== 学生信息卡片 ==========
学号:             20240101001
年龄:             20
年级等级:         A
入学年份:         2024
GPA:              3.85
学费:             58000.50
在读状态:         true
奖学金:           false
班级人数:         45

===== 类型大小 =====
short:         2 字节
int:           4 字节
long long:     8 字节
float:         4 字节
double:        8 字节
bool:          1 字节

逐段分析:

  • long long 确保学号不受平台影响(至少 64 位)
  • float 用 f 后缀,double 是默认浮点字面量类型
  • uint32_t 来自 <cstdint>,保证 32 位无符号,跨平台一致
  • std::boolalpha 让 bool 输出为 true/false 而非 1/0
  • sizeof 是编译期运算符,返回类型或变量占用的字节数

示例二:类型溢出与精度陷阱

场景说明:演示整型溢出和浮点精度问题,帮助理解类型边界。

#include <iostream>
#include <limits>
#include <cstdint>

int main() {
    // ---- 整型溢出 ----
    int16_t small_max = 32767;       // int16_t 最大值
    int16_t overflow = small_max + 1; // 溢出!

    std::cout << "===== 整型溢出 =====" << std::endl;
    std::cout << "int16_t 最大值: " << small_max << std::endl;
    std::cout << "最大值 + 1:     " << overflow
              << "  ← 回绕到最小值(有符号溢出是未定义行为)" << std::endl;

    // 无符号溢出是明确定义的(回绕)
    uint16_t unsigned_max = 65535;
    uint16_t unsigned_overflow = unsigned_max + 1;
    std::cout << "uint16_t 最大值 + 1: " << unsigned_overflow
              << "  ← 回绕到 0(定义良好)" << std::endl;

    // ---- 浮点精度 ----
    std::cout << "\n===== 浮点精度 =====" << std::endl;

    float f1 = 0.1f;
    float f2 = 0.2f;
    float sum = f1 + f2;

    std::cout << std::setprecision(10);
    std::cout << "0.1f + 0.2f = " << sum << std::endl;
    std::cout << "是否等于 0.3f? " << std::boolalpha
              << (sum == 0.3f) << "  ← float 无法精确表示 0.1" << std::endl;

    // double 精度更高,但仍是近似
    double d1 = 0.1;
    double d2 = 0.2;
    double dsum = d1 + d2;
    std::cout << "0.1 + 0.2 (double) = " << dsum << std::endl;

    // 大数吃小数
    float large = 1.0e8f;
    float small = 1.0f;
    std::cout << "\n1 亿 + 1 (float) = " << (large + small)
              << "  ← 精度丢失," << small << " 被吞掉了" << std::endl;

    // ---- 类型极限 ----
    std::cout << "\n===== 类型极限 =====" << std::endl;
    std::cout << "int 最大值:     " << std::numeric_limits<int>::max() << std::endl;
    std::cout << "int 最小值:     " << std::numeric_limits<int>::min() << std::endl;
    std::cout << "double 最小值:  " << std::numeric_limits<double>::min() << std::endl;
    std::cout << "double 精度:    " << std::numeric_limits<double>::epsilon()
              << std::endl;

    return 0;
}

预期输出:

===== 整型溢出 =====
int16_t 最大值: 32767
最大值 + 1:     -32768  ← 回绕到最小值(有符号溢出是未定义行为)
uint16_t 最大值 + 1: 0  ← 回绕到 0(定义良好)

===== 浮点精度 =====
0.1f + 0.2f = 0.3000000119
是否等于 0.3f? false  ← float 无法精确表示 0.1
0.1 + 0.2 (double) = 0.30000000000000004

1 亿 + 1 (float) = 100000000  ← 精度丢失,1 被吞掉了

===== 类型极限 =====
int 最大值:     2147483647
int 最小值:     -2147483648
double 最小值:  2.22507e-308
double 精度:    2.22045e-16

逐段分析:

  • 有符号整型溢出是未定义行为(UB),编译器可能做任何优化假设,不应依赖回绕行为
  • 无符号整型溢出是明确定义的(模运算回绕),但仍应避免
  • 浮点数用 IEEE 754 二进制表示,0.1 在二进制中是无限循环小数,无法精确存储
  • float 约 7 位有效数字,1 亿 + 1 超出精度范围,小数被"吞掉"
  • <limits> 头文件中的 std::numeric_limits<T> 提供每种类型的极限值查询

易错场景与面试考点

易错场景

场景错误表现正确做法
有符号整型溢出回绕值不可预测,编译器可能删除溢出检查使用更大的类型或 unsigned
浮点数用 == 判等0.1 + 0.2 == 0.3 为 false用 fabs(a - b) < epsilon 比较
浮点大数吃小数累加结果偏离预期从小到大累加或使用 double
sizeof(long) 依赖平台Windows 4 字节,Linux 8 字节使用 int64_t 固定宽度类型
char 有无符号不确定位运算结果跨平台不一致明确使用 signed char 或 unsigned char
列表初始化窄化转换int x{3.14} 编译报错明确写出转换意图或使用正确类型

常见面试问题

  1. int 占几个字节?——标准只规定最小宽度(16 位),实际大小由实现决定,现代平台上通常为 4 字节(32 位)。

  2. 有符号整型溢出是未定义行为,为什么?——C++ 标准不规定有符号整型的编码方式(虽然绝大多数平台用补码),因此溢出行为未定义,编译器可以做更激进的优化。

  3. 0.1 + 0.2 == 0.3 为什么是 false?如何正确比较浮点数?——0.1 和 0.2 在 IEEE 754 中无法精确表示。应使用 fabs(a - b) < epsilon 判等,epsilon 通常取 std::numeric_limits<double>::epsilon() 的若干倍。

  4. float 和 double 的区别,什么时候用 float?——float 4 字节约 7 位有效数字,double 8 字节约 15 位。图形/游戏中的顶点数据可用 float 节省带宽和缓存;科学计算和金融场景必须用 double。

  5. long 和 long long 的区别?C++11 为什么引入 long long?——long 最小 32 位,long long 最小 64 位。C++11 将 long long 标准化以提供跨平台的 64 位整型。

  6. 列表初始化 {} 相比 = 有什么优势?——禁止窄化转换;不会发生"最令人烦恼的解析"(most vexing parse);统一了各种初始化场景的语法。

小结

  • C++ 基本类型分整型、浮点、字符和其他四大类
  • 使用 <cstdint> 的固定宽度类型消除跨平台差异
  • 列表初始化 {} 是推荐写法,禁止窄化转换
  • 有符号溢出是未定义行为;浮点数不能用 == 判等
下一页
类型转换