一、变量类型及其存储特性

【记忆口诀】三变量,作用域不同;生命周期各有别,存储位置要分清

1.1 静态局部变量、全局变量、局部变量的特点与使用场景

定义与特性

  • 静态局部变量:函数内定义,static修饰;生命周期为整个程序,仅初始化一次,存储在数据段
  • 全局变量:函数外定义;生命周期为整个程序运行区间,程序中任何地方可访问,存储在数据段或BSS段
  • 局部变量:函数内定义;作用域和生命周期仅限于函数体内,每次调用重新创建,存储在栈上

表格对比

分类 局部变量 静态局部变量 全局变量
作用域 当前函数或者代码块内 当前函数内部(外部不能访问) 整个程序内
生命周期 每次进入函数创建,用完就没 程序一运行就存在,一直到程序结束 程序启动时创建,程序结束才销毁
初始化行为 (不赋值则值为随机值,取决于栈区残留数据) (仅初始化一次,基本类型默认值为0,存储在数据段) (基本类型默认初始化为0,存储在数据段或BSS段)
存储位置 栈区(stack),速度快但不持久 数据段或BSS段(非栈),可长期保存 数据段或BSS段,生命周期长
适合场景 做临时运算,比如循环里的变量、临时数组等 想在函数里"记住之前的值",如统计次数、递归深度控制等 整个程序都需要共享的变量,比如配置信息、缓冲区等

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 静态局部变量示例
void counter() {
static int count = 0; // 静态局部变量
count++;
cout << "Static local count: " << count << endl;
}

// 全局变量示例
int globalVar = 100; // 全局变量
void modifyGlobal() {
globalVar += 50;
cout << "Modified global: " << globalVar << endl;
}

// 局部变量示例
void calculate() {
int localVar = 5; // 局部变量
localVar *= 2;
cout << "Local variable: " << localVar << endl;
}

二、指针与引用

【记忆口诀】指针是地址变量,引用是变量别名;指针可变可空,引用必须初始化且不可变

2.1 指针和引用的区别

指针特性

  • 存储另一个变量的内存地址,使用时需解引用(*)访问目标值
  • 可重新赋值指向其他对象,支持指针算术(如++)
  • 可为空(nullptr),有独立内存空间(通常4或8字节)
  • 需要手动管理动态内存

引用特性

  • 变量的别名,绑定后不能修改指向,且不能为空
  • 使用时无需解引用,语法上更简洁
  • 无独立内存空间,与目标变量共享内存
  • 无需手动管理内存

核心区别:不存在指向空值的引用,但存在指向空值的指针

const修饰对比

  • 指针:const int* p(指向常量的指针)、int* const p(指针常量)、const int* const p(指向常量的指针常量)
  • 引用:没有const引用,但可以绑定到const对象(const int&

使用场景

  • 指针:处理可能为NULL的情况(如可选参数)、需要改变指向的对象(如遍历链表)、动态内存分配、多级间接访问
  • 引用:函数参数传递(避免拷贝开销)、必须绑定到有效对象的场景、链式调用、运算符重载

三、数据类型

【记忆口诀】整型长度有规范,short 16 int自然,long 32 long long 64

3.1 整型数据长度标准

C++整型数据长度规范:

  • short 至少 16 位
  • int 至少与 short 一样长
  • long 至少 32 位,且至少与 int 一样长
  • long long 至少 64 位,且至少与 long 一样长

在大多数系统中:

  • short 为 16 位(2字节)
  • int 为 32 位(4字节)
  • long 为 32 位(4字节)
  • long long 为 64 位(8字节)

无符号类型

  • 不存储负数值的整型,可以增大变量能够存储的最大值,数据长度不变
  • int 被设置为自然长度,即计算机处理起来效率最高的长度

四、关键字解析

【记忆口诀】const只读,static控作用域,extern跨文件,define预处理替换

4.1 const关键字

主要作用:指定变量、指针、引用、成员函数等的不可修改性质

常见用法

  • 常量变量:声明常量,使变量的值不能被修改
  • 常量指针(const int* p):指向常量的指针,不能通过指针修改对象的值
  • 指针常量(int* const p):指针本身是常量,不能改变指针指向
  • 常量成员函数:函数不会修改对象的成员变量(除mutable修饰的成员)
  • 常量引用参数:函数不会修改传入的参数

4.2 static关键字

主要作用:控制变量和函数的生命周期、作用域以及访问权限

不同场景下的作用

  • 静态局部变量:生命周期延长至程序结束,作用域限于函数内部,仅首次初始化
  • 静态全局变量/函数:限制作用域为当前文件,避免命名冲突
  • 静态成员变量:属于类而非对象实例,所有对象共享同一份数据,必须在类外初始化
  • 静态成员函数:不依赖于对象实例,无this指针,只能访问类的静态成员

4.3 extern关键字

主要作用:声明全局变量或函数,实现跨文件共享

常见用法

  • 变量声明:extern int x;(声明不分配内存,定义需在其他文件中)
  • C++与C混合编程:extern "C" { /* C函数声明 */ }
  • 防止重复定义:在头文件中用extern声明变量,源文件中定义

4.4 define和typedef的区别

define

  • 简单的字符串替换,无类型检查
  • 编译预处理阶段起作用
  • 不分配内存,多次使用多次替换

typedef

  • 有对应的数据类型,进行类型检查
  • 编译、运行时起作用
  • 在静态存储区分配空间,程序运行过程中内存中只有一个拷贝

4.5 define和inline的区别

define

  • 预编译时处理的宏,简单字符串替换,无类型检查

inline

  • 编译时将函数体直接插入调用处,减少函数调用开销
  • 是一种特殊函数,进行类型检查
  • 对编译器的请求,可能被拒绝

C++中inline编译限制

  1. 不能存在任何形式的循环语句
  2. 不能存在过多的条件判断语句
  3. 函数体不能过于庞大
  4. 内联函数声明必须在调用语句之前

4.6 constexpr和const

const:表示"只读"的语义,可定义编译期常量或运行期常量

constexpr:表示"常量"的语义,只能定义编译期常量

关系:标记为constexpr的变量或成员函数自动成为const,但反之不成立

constexpr变量:必须使用常量初始化,如constexpr int MOD = 1000000007;

constexpr函数:函数的返回类型和所有形参类型都是字面值类型,C++14起允许函数体包含多条语句

五、内存管理

【记忆口诀】new/delete是运算符,调用构造与析构;malloc/free是函数,仅管内存不处理对象

5.1 new和malloc的区别

  1. new内存分配失败时抛出bac_alloc异常,不返回NULL;malloc失败时返回NULL
  2. new无需指定内存块大小;malloc需要显式指出所需内存尺寸
  3. operator new/operator delete可重载;malloc/free不可重载
  4. new/delete会调用对象的构造函数/析构函数;malloc不会
  5. malloc/free是C++/C标准库函数;new/delete是C++运算符
  6. new从自由存储区分配内存;malloc从堆上分配内存

5.2 std::atomic

作用:提供原子操作,解决多线程中的数据竞争问题

背景:看似原子的操作(如a++、int a = b)在汇编级别实际上是多条指令,在多线程环境下可能导致不确定结果

使用示例

1
2
std::atomic<int> value;
value = 99;

注意:C++11起,局部静态变量的初始化是线程安全的

六、函数与操作符

【记忆口诀】函数指针指向函数,指针函数返回指针;前置++效率高,后置++需临时对象

6.1 函数指针

定义:指向函数的指针变量,用于存储函数的地址,允许在运行时动态选择调用的函数

声明形式返回类型 (*指针变量名)(参数列表)

使用场景

  • 回调函数
  • 函数指针数组(实现状态机)
  • 动态加载库
  • 多态实现
  • 函数指针作为参数

示例

1
2
3
int add(int a, int b) { return a + b; }
int (*operationPtr)(int, int) = &add;
int result = operationPtr(10, 5);

6.2 函数指针和指针函数的区别

函数指针:指向函数的指针变量,如int (*ptr)(int, int)

指针函数:返回指针类型的函数,如int* getPointer()

6.3 前置++与后置++

前置++实现:返回引用,效率更高

1
2
3
4
self &operator++() {
node = (linktype)((node).next);
return *this;
}

后置++实现:返回临时对象,开销较大

1
2
3
4
5
const self operator++(int) {
self tmp = *this;
++*this;
return tmp;
}

后置++特点

  • 通过添加无意义的int占位参数区分
  • 返回对象而非引用(因为临时对象会被销毁)
  • 通常加const修饰防止连续调用(如i++++)

七、类与结构体

【记忆口诀】struct默认公有,class默认私有;继承方式也不同,struct默认公有继承

7.1 struct和Class的区别

相同点

  • 如果没有定义任何构造函数,编译器都会生成默认的无参数构造函数

不同点

  • struct成员默认是公有的(public);class成员默认是私有的(private)
  • struct继承时默认使用公有继承;class继承时默认使用私有继承
  • struct通常用于表示一组相关的数据;class通常用于表示封装了数据和操作的对象

八、类型转换

【记忆口诀】四种转换各不同,static_cast最常用;dynamic_cast带检查,reinterpret_cast最危险;const_cast去常量

8.1 C++强制类型转换

static_cast

  • 没有运行时类型检查,安全性取决于转换的合理性
  • 用于基本数据类型之间的转换,上行转换(派生类→基类)安全
  • 下行转换(基类→派生类)不安全

dynamic_cast

  • 进行下行转换时具有类型检查功能,更安全
  • 要求转换类型是类的指针、引用或void*,基类必须有虚函数
  • 转换失败时返回nullptr(指针)或抛出异常(引用)

reinterpret_cast

  • 可将指针转换为其他类型的指针,整型与指针互转
  • 转换行为依赖平台实现,移植性差

const_cast

  • 将常量指针转换为非常量指针,常量引用转换为非常量引用
  • 去掉类型的const或volatile属性

九、其他重要概念

【记忆口诀】nullptr是C++11关键字,类型安全指针空值;sizeof编译期计算,获取大小要记清

9.1 nullptr与NULL的区别

nullptr

  • C++11引入的关键字,表示特殊的空指针类型(std::nullptr_t
  • 可隐式转换为任意指针类型,但不能转换为整数类型
  • 类型安全,推荐使用

NULL

  • 宏定义,通常定义为0(void*)0
  • 本质是整数常量,可隐式转换为指针类型,但可能引发歧义
  • 不推荐使用

9.2 sizeof操作符

作用:获取变量或类型所占用的字节数,编译时计算

语法

  • sizeof(type):获取类型大小
  • sizeof(expression):获取表达式结果类型的大小
  • sizeof var:获取变量大小(括号可选)

重要特性

  • 不执行表达式,仅分析类型
  • 数组名作为参数时返回整个数组的大小,但在函数参数中数组退化为指针
  • 空类大小为1字节(C++要求每个对象有唯一地址)
  • 包含虚函数的类大小通常包含虚函数表指针

常见用途

  • 内存分配和释放
  • 数组遍历
  • 跨平台兼容性处理
  • 模板元编程中的类型特征判断