内存分区

四区

  • 代码区:函数体二进制代码,由操作系统进行管理
  • 全局区全局变量、静态变量、常量
  • 栈区:编译器自动分配释放,存放函数的参数值,局部变量
  • 堆区程序员分配和释放,拖程序员不释放,程序结束时由操作系统回收

程序运行前

编译后,生成exe可执行程序,未执行该程序前,分为两个区域:

1.代码区

存放CPU执行的机器指令
共享、只读

2.全局区

该区域的数据在程序结束后由操作系统释放
全局区中存放:

  • 全局变量
  • 静态变量(static)
  • 常量

常量区:全局区里的一个特定子区域,专门存放编译期间就能确定的常量值
存放:const修饰的全局变量字符串常量

程序运行后

3.栈区

编译器自动分配释放
存放:

  • 函数的参数值
  • 局部变量

注意不要返回局部变量 的地址,栈区开辟的数据由编译器自动释放
返回后编译器会保留一次,但再访问就会出错,因为栈区数据已经释放了

4.堆区

程序员分配内存,若程序员不释放,则程序结束时由操作系统回收
new 开辟内存
利用new关键字,可以将数据开辟到堆区

int * p = new int(10); // 堆区数据**的地址**用栈区指针保存

new返回的是该数据类型的指针

new和delete

堆区数据由管理员开辟和释放
释放堆区数据,利用关键字 delete

int * func()
{
	int *p = new int(10); // new 开辟到堆区 在堆区创建整型数据
	
	return p; // 返回指针
}

void test()
{
	int *p = func(); // new p
	cout<< *p <<endl;
	cout<< *p <<endl;
	cout<< *p <<endl; // 由管理员释放
	
	delete p; // delete p 释放
	
	cout<< *p <<endl; // 内存已释放,**非法**访问  报错,且打印不出来
}

// 在堆区用new开辟数组
void test2()
{
	int * arr = new int[10]; // 10个元素的数组,返回首地址
	
	for(int i=0;i<10;i++){
		arr[i] = i+1 // 赋值;
	}
	
	// 释放堆区数组
	delete[] arr; // []
}

引用

作用:给变量取别名
语法:数据类型 &别名 = 原名(即变量名)

注意事项

  • 引用必须初始化
  • 引用初始化后不能更改
int a = 10;
int b = 20;

// int &c; 错误

int &c = a;
c = b; // 赋值操作,不是更改引用(c是a的别名,两者共用一个内存空间)

引用做函数参数(引用传递)

形参修饰实参
优点:可以简化指针修改实参

(值传递,地址传递都学过了)
引用传递:

void swap(int& a, int& b){
	int temp = a;
	a = b;
	b = temp;
}

int main(){
	int a = 10;
	int b = 20;
	
	swap(a,b);
	
	cout<<a<<endl<<b<<endl;

	return 0;
}

效果和地址传递相同,只是语法更清楚简单

引用做函数返回值

引用可以作为函数返回值

注意:不要返回局部变量引用 (栈区 编译器自动释放)
函数调用可作左值

int& test1(){
	int a = 10; // 局部变量存放在**栈区**
	return a;
}

![[函数调用作左值.png]]
![[函数调用作左值输出.png]]

test调用完,返回引用(变量)
相当于 a = 1000;
ref2就是a的别名

如果函数的返回值是引用,这个函数调用可作左值

引用的本质

引用在c++内部实现是一个指针常量

// 发现是引用,转换为 int* const ref = &a;

指针常量:指向不能变,值可以变
这也就是了为什么引用在初始化后不能更改,而可以赋值

![[引用的本质.png]]

编译器扛下了所有~

常量引用

作用:主要用来修饰形参,防止误操作

在函数参数列表中,加const修饰形参,防止形参改变实参

// int & ref = 10; // 非法
// 引用必须引一块**合法的内存空间**
// 10、3.14、'a' 这样的**纯字面量**,是直接嵌入在机器指令的**立即数**中的。**没有独立的内存地址**。

const int & ref = 10; // 加上const
// 编译器将代码改为:
int temp = 10;
const int & ref = temp;
// 编译器帮你取原名

const 只读,不允许修改

void showValue(int &val){
	val = 1000; // 引用传递=地址传递 更改了实参
	
	cout<<val<<endl;
}

int main(){
	int a = 100;
	showValue(a);
	
	cout<< a <<endl; // 1000
	
	return 0;
}

// 防止误操作
void showValue(const int &val){ // 加const修饰
	// 不能修改,报错
}

函数的默认参数

函数的参数列表中的形参可以有默认值

语法:返回值类型 函数名 (参数 = 默认值) {}

1.从右往左不能有跳过的
2.函数声明有默认值,则函数实现就不能有默认参数

函数的占位参数

![[占位参数.png]]

函数重载

在这里插入图片描述

注意事项
![[函数重载.png]]![[函数重载注意事项.png]]![[函数重载注意事项2.png]]

结语

关于引用、默认参数、函数重载,还有一些补充的概念,推荐学习这几篇博客:
命名空间、输入输出:
c++入门(上)——lvy
缺省参数(默认参数)、函数重载、引用
c++入门(中)——lvy
引用vs指针、内联函数、auto关键字、范围for
c++入门(下)——lvy

终于,我们将来到类和对象~也是c++的核心思想:面向对象

更多推荐