> 文章列表 > 【C++核心】内存、引用、函数

【C++核心】内存、引用、函数

【C++核心】内存、引用、函数

一、内存四区域

C++程序在执行时,将内存大方向划分为4个区域
程序运行前分为:

  • 代码区:存放函数体的二进制代码exe,由操作系统进行管理的

exe机器指令、共享、只读

  • 全局区:存放全局变量和静态变量以及常量(字符串常量(用“”引起来的) & 全局常量(放在全局中,用const修饰的))

全局变量、静态变量、常量。该区域的数据在程序结束后由操作系统释放

程序运行后:

  • 栈区:由编译器自动分配释放(不用程序员管理他的生或死), 存放函数的参数值(形参数据也会放在栈区),局部变量(函数内部的)等
  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

堆栈区

从硬件上说,堆和栈最终都是内存条上的若干存储单元,所以并没有什么不同。

但,由于很多CPU对压栈、出栈操作有硬件(指令)上的支持,所以在栈区分配/归还内存速度极快(相比之下,堆上分配简直是龟速);尤其是函数内部的局部变量,可以轻易与函数调用/返回绑定,因此几乎所有编译型语言都会在利用栈管理局部变量(而且会优先使用空闲的寄存器,所以几乎所有高级语言都是访问局部变量速度最快)。
栈内存是存取速度比堆要快,仅次于寄存器,栈数据可以共享。

不仅如此。由于栈内存分配/回收的特殊机制,使得同一函数内部的“局部变量”总是分配在同一段连续内存空间上的;那么引用这些局部变量就不太容易出现CPU缓存失效问题。

特点不同:堆内存是优先队列的一种数据结构,满足先进后出的性质,栈内存存取速度仅次于寄存器,栈数据可以共享。
范围不同:堆内存需要手动进行适当,如果不释放,会出现内存溢出的问题。栈内存会随着变量的作用域分配和释放。

栈区:
栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

​ 由编译器自动分配释放, 存放函数的参数值,局部变量等
​ 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

【C++核心】内存、引用、函数
第一次可以打印正确的数据是因为编译器做了保留。
在func运行完之后就把存a的地址直接释放了,内存被释放,用p来*就是非法操作。


堆区:

​ 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
​ C ++中利用new操作符在堆区开辟数据

语法: new 数据类型
利用new创建的数据,会返回该数据对应的类型的指针
delete后面加[]才能告诉编译器你要删除的是一系列的内存空间,而不是arr所指向的那一个地点
int* p = new int(10) 创建一个int类型的变量,存放10;<—> delete p
int* arr = new int[10] 创建一个10个整形数据长度的数组;<—>delete[] arr;

Notes:
1、指针a本质上也是局部变量,所以是放在栈区的。
2、指针保存的数据,放在堆区,堆区的特点是由程序员分配释放。

【C++核心】内存、引用、函数
【C++核心】内存、引用、函数


二、&引用

别名,就是小名,指的都是同一个人。
引用的本质在c++内部实现是一个指针常量.
int& ref = a自动转换为 int* const ref = &a;
指针的指向不变,这也说明了为什么引用的两个特点:
1、一旦初始化后,不能改方向。定义的时候必须给定是对谁取别名。
2、不可以再改方向,所有操作均视为赋值

#include<iostream>
using namespace std;
int& test0()
{//a是局部变量,放在栈中,由编译器自动分配释放int a = 10;return a;
}
int& test1()
{//a是静态变量,放在全局内存中,其特点是所有程序结束后由操作系统释放static int a = 10;return a;
}
int main() {int& ref = test0();//返回的是a的一个别名,用同样的数据类型接住cout << ref << endl;//编译器保留一次cout << ref << endl;//因为ref是a的一个别名,而a已经被释放了,所以乱码int& ref2 = test1();cout << ref2 << endl;cout << ref2 << endl;test1() = 100;//test1()作为a的别名可以当成左值。//被赋值,而ref2又是a的别名。cout << ref2 << endl;cout << ref2 << endl;return 0;
}

【C++核心】内存、引用、函数

常量引用

一般用于修饰形参,防止误操作。
在最前面加个const 就代表约束了这个参数,只读不能改!
类似于const int * p约束指针
const int& p 本质上<——>const int * const p
p只读,不可改(大小、方向)。
【C++核心】内存、引用、函数
实验:

#include<iostream>
using namespace std;void test(const int *p)
{//a是静态变量,放在全局内存中,其特点是所有程序结束后由操作系统释放static int a = 10;cout << *p << endl;cout << p << endl;p = &a;cout << *p << endl;cout << p << endl;
}
int main() {int a = 1;int* p = &a;cout << *p << endl;cout << p << endl;cout << "*****before*****" << endl;test(p);cout << "*****after*****" << endl;cout << *p << endl;cout << p << endl;return 0;
}

【C++核心】内存、引用、函数
结论:
函数的形参是在栈区内重开一个指针,和原来在main函数里面的指针一样指向某个变量。指针就是个地址。
值和地址的关系是一对多。多个指针指向同一个地址,有的时候要求函数内的指针是只读的,不能改变值,所以要在前面加const。
形参里面的指针方向具体怎么改不影响其他指针,方向对于每个指针来说是独立的,但是访问地址的值并修改,会影响大家,所以要约束一些指针的权限为只读

三、函数参数

1、参数
在C++中,函数的形参列表中的形参是可以有默认值的。

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

如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
声明和实现中只能有一个有默认参数,otherwise对于编译器来说,具有二义性
二义性是函数调用的首要需要避免的问题。

int func2(int a, int b = 10, int c);//报错
//这是个函数声明用;结尾,好处是func2可以在任意地方写

2、重载
就是不同的参数会被认为是不同的函数,即便名字相同
以下视为不同的函数

void func()
{cout << "func 的调用!" << endl;
}
void func(int a)
{cout << "func (int a) 的调用!" << endl;
}