C++中的内存
内存分区模型
- C++在执行程序时,将内存大致分成四个区域
○ 代码区:存放函数体的二进制代码,由操作系统进行管理
○ 全局区:存放全局变量和静态局部变量以及常量
○ 栈区:由编译器自动分配和释放,存放函数的参数值,局部变量等
○ 堆区:由程序员分配和释放,若程序员不释放,则程序结束时由操作系统释放 - 内存四区的意义:
○ 不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程
程序运行前
- 在程序编译后,生成了.exe 结尾的可执行程序,未执行该程序前分为两个区域
- 代码区:
○ 存放CPU执行的机器命令
○ 代码区是共享的,共享的目的是对于频繁执行的程序,只需要在内存中有一份代码即可;
○ 代码区是只读的,是只读的原因是防止程序意外地修改了它的指令 - 全局区:
○ 全局变量和静态变量存放在此
○ 全局区还包含了常量区、字符串常量和其它常量也存放于此
○ 该区域的数据在程序结束后由操作系统释放 - 常量区中:
○ 字符串常量
○ const 修饰的全局常量
程序运行后
- 栈区:
○ 由编译器自动分配释放,存放函数的参数值,局部变量等
○ 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放 - 堆区:
○ 由程序员分配和释放,若程序员不释放,则程序结束时由操作系统释放
○ 在C++中用 new 开辟内存空间
//new返回的是地址,所以用指针接收
int * p = new int (1);
new 运算符
● 在C++中用new在堆上开辟空间;
● 用 delete 释放堆上的空间;
//new运算符的使用
#include <iostream>
using namespace std;
int main() {
/* new 返回该数据类型的指针
* 用相应类型的指针接收
*/
int * p = new int(1);
cout<<"堆区开辟数据:*p= "<<*p<<endl;
//用delete释放堆上的空间
delete p;
/* 开辟数组
*/
int * arr = new int[3];
for(int i = 0;i<3;i++)
{
arr[i] = i+1;
cout<<"arr["<<i<<"]="<<arr[i]<<endl;
}
//释放堆上的数组
delete []arr;
return 0;
}
/*
堆区开辟数据:*p= 1
arr[0]=1
arr[1]=2
arr[2]=3
*/
引用
● 作用:给变量起别名
● 语法:&别名 = 变量;
//
// Created by NJUST'er'wang's on 2022/1/13.
//引用
#include <iostream>
using namespace std;
int main() {
//引用的基本语法
int a = 10;
int &b = a;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout<<"----------"<<endl;
b = 100;
cout << "a = " << a << endl;
cout << "b = " << b << endl;
return 0;
}
/*
a = 10
b = 10
----------
a = 100
b = 100
*/
- 注意事项:
○ 引用必须初始化:
int &b; //❌
int &b = b1; //√
○ 初始化后不能更改
int a = 1;
int &b = a;
int c = 2;
b = c; //这儿不是更改别名,而是赋值
/*
a = 2 b = 2 c = 2
*/
引用做函数的参数
○ 作用:函数传参时,可以利用引用的技术让形参修饰实参
○ 优点:可以简化指针修改实参
//
// Created by NJUST'er'wang's on 2022/1/13.
//引用和函数
#include <iostream>
using namespace std;
void swap01(int a,int b){
int t =a;
a = b;
b = t;
}
void swap02(int * a,int * b){
int temp = *a;
*a = *b;
*b = temp;
}
void swap03(int &m,int &n){
int temp = m;
m = n;
n = temp;
}
int main()
{
int a = 1,b =2;
cout<<"初始值:"<<' ';
cout<<"a= "<<a<<' '<<"b= "<<b<<endl;
//值传递形参不修饰实参
cout<<"值传递:"<<' ';
swap01(a,b);
cout<<"a= "<<a<<' '<<"b= "<<b<<endl;
//地址传递形参修饰实参
cout<<"地址传递:"<<' ';
swap02(&a,&b);
cout<<"a= "<<a<<' '<<"b= "<<b<<endl;
swap02(&a,&b);//将值更正
//引用形参修饰实参
cout<<"引用:"<<' ';
swap03(a,b);
cout<<"a= "<<a<<' '<<"b= "<<b<<endl;
return 0;
}
/*
初始值: a= 1 b= 2
值传递: a= 1 b= 2
地址传递: a= 2 b= 1
引用: a= 2 b= 1
*/
引用做函数返回值
//
// Created by NJUST'er'wang's on 2022/1/13.
//引用做函数返回值
#include <iostream>
using namespace std;
int &fun() {
static int a = 8848;
return a;
}
int main() {
int &ret = fun();
cout << "ret= " << ret<<endl;
cout<<"------------"<<endl;
fun() = 10086;
cout << "ret= " << ret;
return 0;
}
/*
ret= 8848
------------
ret= 10086
*/
引用的本质
○ 在C++内部实现是一个指针常量
/*
指针常量:指针的指向不能改变,内容可改
*/
int a = 1;
int &b = a; //内部:int * const b = a;
int c = 2;
b = c; //内部:*b = c;
○ 结论:C++推荐使用引用,本质上是指针,不过指针的操作都由编译器完成
常量引用
○ 使用场景:用来修饰形参,防止误操作
void fun(const int &b) //安全,防止数据被修改
{
cout<<"b = "<<b;
}
int main()
{
int a = 1;
fun(a);
return 0;
}
函数
函数的默认参数
//
// Created by NJUST'er'wang's on 2022/1/13.
//函数的默认参数
#include <iostream>
using namespace std;
int func(int a, int b = 2, int c = 3) {
return a + b + c;
}
int main() {
cout << "sum = " << func(1);
return 0;
}
/*
sum = 6
*/
○ 从左往右的第一个有默认参数的形参,其右边的所有形参都必须有默认值;
○ 函数声明时有默认参数,则实现的时候不能有默认参数
- 函数的占位参数
○ 语法:返回值类型 函数名(数据类型)
○ 默认占位参数:返回值类型 函数名(数据类型 = value)
void fun(int ){
}
函数重载
○ 作用:函数名可以相同,提高重复性
○ 条件:
■ 同一个作用域下
■ 函数名称相同
■ 函数 参数 类型不同 或者 个数不同 或者 顺序不同
■ 注意:函数的返回值不可以作为函数重载的条件
○ 注意事项:
■ 引用作为重载的条件
■ 函数重载碰到默认参数
//
// Created by NJUST'er'wang's on 2022/1/13.
//引用作为重载
#include <iostream>
using namespace std;
void fun(int &a) {
cout << "fun with int" << endl;
}
void fun(const int &a) {
cout << "fun with const int" << endl;
}
int main() {
int a = 10;
fun(a); //a 是一个变量,可读可写,故调用前者
fun(10); //10是常量,只可读
return 0;
}
/*
fun with int
fun with const int
*/
#include <iostream>
using namespace std;
void fun(int &a = 10) { //不合法
cout << "fun with int" << endl;
}
void fun(const int &a = 10) { //合法
cout << "fun with const int" << endl;
}