C++ 内存的分区模型、引用、函数补充
本文最后更新于:2022年4月22日 上午
C++ 内存的分区模型、引用、函数补充
内存的分区模型
代码区
所有编写的代码都放在代码区,存放函数体的二进制代码,由操作系统进行管理
存放 cpu
执行的机器指令
代码区是共享的,共享的是对于频繁执行的程序,只需要在程序中有一份代码即可
代码区是只读的,防止程序意外的修改了他的指令
全局区
全局变量/ 通过
const
修饰的全局常量通过
static
关键字修饰的静态变量字符串常量
int g_num1 = 10;
int g_num2 = 20;
int main() {
// 局部变量
int l_num1 = 10;
cout << "局部变量 l_num1 地址" << (int)&l_num1 << endl << endl;
// 可以看到内存地址是相近的
// 全局变量
cout << "全局变量 g_num1 地址" << (int)&g_num1 << endl;
cout << "全局变量 g_num2 地址" << (int)&g_num2 << endl;
// static 关键字修饰的静态变量
static int l_num2 = 10;
cout << "static 修饰的静态变量 l_num2 地址" << (int)&l_num2 << endl;
// 字符串常量
cout << "字符串变量变量 l_name 地址" << (int)&"hello world" << endl;
// 全局常量
cout << "全局常量 g_num3 地址" << (int)&g_num3 << endl;
return 0
}
输出
局部变量 l_num1 地址6878980
全局变量 g_num1 地址7286784
全局变量 g_num2 地址7286788
static 修饰的静态变量 l_num2 地址7286796
字符串变量变量 l_name 地址7273596
全局常量 g_num3 地址7273264
从以上可以看出,全局区的变量存放地址是相近的
栈区
由编译器自动分配和释放,存放函数的形参列表,局部变量,当函数执行完毕时会销毁,所以不要在 函数中返回 函数内变量的内存地址
// 返回 一个 int 型的指针
int* get_a() {
int a = 10;
return &a;
}
int main() {
int l_num3 = 20;
int* l_a = get_a();
// 第一次可以取到值,第二次取不到,第一次编译器会帮我们做一次保留
cout << "函数返回的指针:" << *l_a << endl;
cout << "函数返回的指针:" << *l_a << endl;
return 0;
}
输出
函数返回的指针:10
函数返回的指针:2062064008
堆区
由开发人员自己分配,通过 new
关键字创建的变量会存储在堆内存空间中
如果需要释放时,由开发人员手动通过 delete
关键字释放,如果没有手动释放,则会在程序运行结束时自动释放
// 返回 对应类型的指针
int* l_num4 = new int(30);
cout << "局部变量 l_num3 地址" << (int)&g_num3 << endl;
输出
局部变量 l_num3 地址7273264
new 关键字的基本使用
int* l_num6 = new int(10);
// 会一直保留,如果没有手动释放则会一直存在,知道程序关闭
cout << "通过 new 创建的变量:" << *l_num6 << endl;
cout << "通过 new 创建的变量:" << *l_num6 << endl;
// 释放内存
delete l_num6;
// 空指针报错
// cout << "通过 new 创建的变量:" << *l_num6 << endl; // 使用未初始化的内存
释放数组
int* arr = new int[3];
//int* arr = new int[3]{ 1,2, 3 }; // 初始化列表
cout << arr << endl;
// 释放数组的内存,需要加上 []
delete[] arr;
引用
引用就类似于给变量起一个别名,通过这个别名去对原数据进行操作时,两个变量都会发生改变,应该指向的是同一块内存地址
需要注意引用必须初始化
基本使用
// 引用的基本使用,理解为起别名
int l_num7 = 10;
// 必须初始化值,且不建议再次修改(因为是内存地址,所以会将原来的值也改变)
int& l_num8 = l_num7;
l_num8 = 20;
// 都输出 20
cout << "l_num7:" << l_num7 << endl;
cout << "l_num8:" << l_num8 << endl;
输出
l_num7:20
l_num8:20
函数的补充
引用作为函数的参数
引用作为函数参数,操作会更为简单
// 函数的引用参数
void scaleUp(int& num) {
// 以下是 num = num * 10; 的简写,不要和指针的操作混淆
num *= 10;
}
int main() {
int l_num9 = 10;
scaleUp(l_num9);
cout << "l_num9:" << l_num9 << endl;
return 0;
}
输出
l_num9:100
返回值是引用的注意点
不要返回局部变量的引用
// 返回 一个 int 型的引用
int b = 10;
int& get_b() {
return b;
}
int main() {
int l_num10 = get_b();
cout << "l_num10:" << l_num10 << endl;
cout << "l_num10:" << l_num10 << endl;
cout << "l_num10:" << l_num10 << endl;
cout << "l_num10:" << l_num10 << endl;
cout << "l_num10:" << l_num10 << endl;
cout << "l_num10:" << l_num10 << endl;
cout << "l_num10:" << l_num10 << endl;
cout << "l_num10:" << l_num10 << endl;
return 0;
}
输出
l_num10:10
l_num10:10
l_num10:10
l_num10:10
l_num10:10
l_num10:10
l_num10:10
l_num10:10
函数的调用作为左值
如果函数返回的是一个引用,则这个函数的调用可以作为左值
// 返回 一个 int 型的引用
int b = 10;
int& get_b() {
return b;
}
int main() {
get_b() = 20;
int l_num11 = get_b();
cout << "l_num11:" << l_num11 << endl;
return 0;
}
输出
l_num11:20
引用的本质
引用的本质就是一个指针常量,指针常量的语法糖
// 引用的本质(就类似于 使用 const 关键字修饰了的指针)
void scaleUp20(int* const num) {
*num *= 20;
}
int main() {
int l_num12 = 10;
scaleUp20(&l_num12);
cout << "l_num12:" << l_num12 << endl;
return 0;
}
输入
l_num12:200
函数形参的默认值
很多语言(java,js)都有函数形参的默认值,用户没有传入的时候可以使用默认值
int sum1(int num1, int num2 = 20) {
return num1 + num2;
}
int main() {
cout << "函数的默认参数:" << sum1(10) << endl;
return 0;
}
输出
函数的默认参数:30
需要注意的是,当某一个形参有默认值后,该形参后面的所有形参都必须拥有默认值,不能像 js
一样使用 undefined
来占位以使用形参默认值
函数的声明和函数的定义只允许一个地方出现默认值
函数的占位参数
// 占位参数,后续很有用
int sum3(int num1, int) {
return num1;
}
函数的重载
java
也有函数的重载,函数的重载需要符合以下三个条件
处于同一作用域
函数名称相同
函数的参数个数不同 或者 参数的类型不同
需要注意的是 函数的返回值不能作为重载的条件,函数的引用参数可以形成重载 通过 const
修饰
函数的重载碰到默认参数,会出现二义性,尽量避免这种情况
基本使用
// 函数的重载
int calcTotal(int num1, int num2) {
cout << "calcTotal 两个参数" << endl;
return num1 + num2;
}
int calcTotal(int num1, int num2, int num3) {
cout << "calcTotal 三个参数" << endl;
return num1 + num2 + num3;
}
int main() {
int l_num13 = 10;
int l_num14 = 10;
int l_num15 = 10;
cout << "函数的重载1:" << calcTotal(l_num13, l_num14) << endl;
cout << "函数的重载2:" << calcTotal(l_num13, l_num14, l_num15) << endl;
cout << "函数的重载3:" << calcTotal(l_num13, l_num14, 10) << endl<< endl;
return 0;
}
输出
calcTotal 两个参数
函数的重载1:20
calcTotal 三个参数
函数的重载2:30
calcTotal 三个参数
函数的重载3:30
函数的引用参数构成重载
// 引用参数构成 重载
int func(int* num) {
cout << "func函数 int* num :" << *num << endl;
return *num;
}
int func(const int* num) {
cout << "func函数 const int* num :" << *num << endl;
return *num;
}
int main() {
const int l_num16 = 10;
cout << "函数的重载1:" << func(&l_num13) << endl;
cout << "函数的重载2:" << func(&l_num16) << endl;
return 0;
}
输出
func函数 int* num :10
函数的重载1:10
func函数 const int* num :10
函数的重载2:10
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处。