内存分区模型
C++程序在执行时,将内存大方向划分为4个区域
- 代码区:存放函数体的二进制代码,由操作系统进行管理
- 全局区:存放全局变量和静态变量以及常量
- 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
| 区域 |
存储内容 |
生命周期 |
访问速度 |
| 栈区 |
局部变量、函数参数、返回地址 |
函数结束时自动释放 |
最快 |
| 堆区 |
new/malloc动态分配的对象 |
手动管理或程序结束回收 |
较慢 |
| 全局区 |
全局/静态变量、常量、虚函数表 |
程序启动到结束 |
中 |
| 代码区 |
机器指令、只读常量(字符串字面量) |
程序运行期间 |
只读 |
代码区(Text Segment)
全局区(Global/Static Segment)
用途:存放全局变量、静态变量(static)、常量
.bss 段:未初始化的全局/静态变量(默认置零)
常量区:字符串常量(如 "Hello")、const 修饰的全局常量
栈区(Stack)
大小固定(Windows默认约1MB,Linux默认约8MB)
堆区(Heap)
用途:存储动态分配内存(通过 new/malloc 申请)
手动管理(需显式使用 delete/free 释放)
内存四区意义:不同区域存放的数据,赋予不同的生命周期,提供更大的灵活编程
典型对比场景
1 2 3 4 5 6 7
| int global_var; void func() { static int static_var; int local_var; int* heap_var = new int(10); const char* str = "Literal"; }
|
⚠️ 注意事项:
- 栈溢出:递归深度过大时可能触发
Stack Overflow
- 内存泄漏:堆区未正确释放内存会导致资源耗尽
- C++11后推荐使用智能指针(如
unique_ptr)管理堆内存
地址分配展示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| using namespace std;
int g_a = 10; int g_b = 20;
const int g_c_a = 10; const int g_c_b = 20;
int main() { int a = 10; int b = 20;
const int c_a = 10; const int c_b = 20; string str = "hello world";
static int s_a = 10; static int s_b = 20;
cout << "局部变量a的地址为:" << "\t" << (long long)&a << endl; cout << "局部变量b的地址为:" << "\t" << (long long) &b << endl;
cout << "局部常量c_a的地址为:" << "\t" << (long long)&c_a << endl; cout << "局部常量c_b的地址为:" << "\t" << (long long)&c_b << endl; cout << "字符串常量str的地址为:" << "\t" << (long long)&str << endl;
cout << "全局常量g_c_a的地址为:" << "\t" << (long long)&g_c_a << endl; cout << "全局常量g_c_b的地址为:" << "\t" << (long long)&g_c_b << endl; cout << "全局变量g_a的地址为:" << "\t" << (long long) &g_a << endl; cout << "全局变量g_b的地址为:" << "\t" << (long long) &g_b << endl;
cout << "静态变量d_a的地址为:" << "\t" << (long long) &s_a << endl; cout << "静态变量d_b的地址为:" << "\t" << (long long) &s_b << endl;
return 0; }
|
记忆技巧:
程序运行前
在程序编译后,生成了.exe可执行程序,未执行该程序前分为两个区域:
代码区:
存放CPU执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
代码是只读,使其只读的原因是防止程序意外地修改了它的指令
全局区:
全局变量和静态变量存放在此
全局区还包含了常量区,字符串常量和其他常量也存放在此
该区域的数据在程序结束后由操作系统释放
程序运行后
栈区
栈区:
- 由编译器自动分配释放,存放函数的参数值,局部变量等
- 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream> #include <string> #include <vector>
using namespace std;
int* func() { int a = 10; return &a; }
int* another_function() { int a = 10; return &a; };
int main() { int* p = func();
cout << *p << endl; std::vector<int> v(1000); cout << *p << endl;
return 0; }
|
堆区
堆区:
- 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
- 在C++中主要利用new在堆区开辟内存
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <string> #include <vector>
using namespace std;
int* func() { int* a = new int(10); return a; }
int main() { int* p = func();
cout << *p << endl; std::vector<int> v(1000); cout << *p << endl;
return 0; }
|
new操作符
C++中利用new操作符在堆区开辟数据
堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete
语法:new 数据类型
示例:
基础用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream> #include <string>
using namespace std;
int main() {
int* p1 = new int(10); cout << *p1 << endl; delete p1;
int* p2 = new int[10]; for (int i = 0; i < 10; i++) { p2[i] = i; cout << p2[i] << " "; } delete[] p2;
return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| #include <iostream> #include <string> #include <vector>
using namespace std;
int* func_int() { int* a = new int(10); return a; }
bool* func_bool() { bool* a = new bool(true); return a; }
string* func_string() { string* a = new string("hello"); return a; }
float* func_float() { float* a = new float(3.14); return a; }
int main() { int* int_p = func_int(); bool* bool_p = func_bool(); string* string_p = func_string(); float* float_p = func_float();
std::vector<int> v(1000); cout << *int_p << endl; cout << *bool_p << endl; cout << *string_p << endl; cout << *float_p << endl;
return 0; }
|
引用
作用:给变量起别名(实际上就是让多个变量指向同一个内存地址)
语法:数据类型& 别名 = 原名
区别:取址符为数据类型 变量名 = &变量名
示例:
1 2 3 4 5 6 7
| int main() { int a = 10; int &b = a; cout << "a:" << a <<endl; cout << "b:" << b <<endl; }
|
引用变量b在底层是通过常量指针(int* const)实现的,存储的是变量a的地址
任何对b的操作(如b = 20)会被编译为*b = 20(自动完成指针解引用)
引用注意事项
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <iostream> #include <string>
using namespace std;
int main() {
int a = 10; int b = 20; int &c = a; c = b;
cout << "a: " << a << endl; cout << "b: " << b << endl; cout << "c: " << c << endl;
return 0; }
|
引用做函数参数
作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include <iostream> #include <string>
using namespace std;
void swap(int a,int b) { int tmp = a; a = b; b = tmp; };
void swap_byPointer(int* a, int* b) { int tmp = *a; *a = *b; *b = tmp; }
void swap_byRef(int& a, int& b) { int tmp = a; a = b; b = tmp; }
int main() {
int a = 10; int b = 20; swap(a,b); cout << "a = " << a << endl; cout << "b = " << b << endl;
int c = 10; int d = 20; swap_byPointer(&c,&d); cout << "c = " << c << endl; cout << "d = " << d << endl;
int e = 10; int f = 20; swap_byRef(e,f); cout << "e = " << e << endl; cout << "f = " << f << endl;
return 0; }
|
通过引用参数产生的效果同按地址传递是一样的
引用做函数返回值
作用:引用是可以作为函数的返回值存在的
注意:不要返回局部变量引用
用法:函数调用作为左值
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include <iostream> #include <string>
using namespace std;
int& test01() { int a = 10; int &b = a; return b; }
int& test02() { int a = 10; return a; }
int& test03() { static int a = 10; return a; }
int main() {
int &a_01 = test01(); int &a_02 = test02(); int &a_03 = test03(); vector<int> v(1000); cout << a_01 << endl; cout << a_02 << endl; cout << a_03 << endl;
test03() = 10000; cout << a_03 << endl;
return 0; }
|
当函数声明为返回int&时,任何返回的int变量都会隐式转换为引用(即使未显式使用&)。其他类型同理
当函数的返回值为引用类型时,是可以作为变量的左值的(也就是对函数内被引用的值重新赋值)
引用的本质
之前已经提到过引用本质,这一章会详细说明
本质:引用的本质在C++内部实现是一个指针常量
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream> #include <string>
using namespace std;
void func(int& ref) { ref = 100; }
int main() {
int a = 100;
int& ref = a; ref = 20;
cout << "a:" << a << endl; cout << "ref:" << ref << endl;
func(ref);
return 0; }
|
C++推荐使用引用技术,因为语法方便,引用本质是指针常量,但所有的指针操作编译器都会帮助程序员完成
常量引用
作用:常量引用主要用来修饰形参,防止误操作
在函数形参列表中,可以加const修饰形参,防止形参改变实参
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <iostream> #include <string>
using namespace std;
void func(const int& ref) { cout << ref << endl; }
int main() {
const int& ref = 10; cout << ref << endl; func(100);
return 0; }
|
需要注意,常量引用是可以直接赋值(字面量 100、表达式结果、临时对象)的,非常量引用则会报错
函数进阶
函数默认参数
在C++中,函数的形参列表中的形参是可以有默认值的
语法:返回值类型 函数名 (参数 = 默认值) {}
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| #include <iostream> #include <string>
using namespace std;
int func_01(int a, int b = 10, int c = 20) { return a + b + c; }
int func_02(int a = 10, int b = 10); int func_02(int a, int b) { return a + b; }
int main() { int getFunc01 = func_01(1); cout << getFunc01 << endl; getFunc01 = func_01(1, 2); cout << getFunc01 << endl;
int getFunc02 = func_02(); cout << getFunc02 << endl;
return 0; }
|
函数占位参数
C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置
语法:返回值类型 函数名 (数据类型) {}
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> #include <string>
using namespace std;
void func(int a, int) { cout << "Hello,Im Func;" << endl; }
int main() { func(10, 10); return 0; }
|
函数重载
在基础篇中已经介绍了基本的函数重载使用,这一章将详细说明
作用:函数名可以相同,提高复用性
函数重载满足条件:
- 同一个作用域下
- 函数名称相同
- 函数参数类型不同,或者个数不同或者顺序不同
注意:函数的返回值不可以作为函数重载的条件
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include <iostream> #include <string>
using namespace std;
void func(int a, int b) { cout << a << " "; cout << b <<endl; }
void func(int a, int b, int c) { cout << a << " "; cout << b << " "; cout << c << endl; }
void func(int a, string b) { cout << a << " "; cout << b << endl; }
void func(string a, int b) { cout << a << " "; cout << b << endl; }
int main() { func(1, 2); func(1, 2, 3); func(1, "hello"); func("hello", 1);
return 0; }
|
函数重载注意事项
示例1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> #include <string>
using namespace std;
void func(int& a) { cout << "func(int& a)函数调用了" << endl; }
void func(const int& a) { cout << "func(const int& a)函数调用了" << endl; }
int main() { int a = 10; func(a); func(10); const int b = 10; func(b);
return 0; }
|
示例2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| #include <iostream> #include <string>
using namespace std;
void func(int a) { cout << "func(int a)函数调用了" << endl; }
void func(int a, int b = 10) { cout << "func(int a, int b = 10)函数调oked" << endl; }
int main() { func(10);
return 0; }
|