Triumph Day1——C++ 内存分区模型、引用、函数提高

目录

1、内存分区模型

1.1 程序运行前

1.2 程序运行后

1.3 new操作符

2、引用

2.1 引用的基本使用

2.2 引用注意事项

2.3 引用做函数参数

2.4 引用做函数返回值

2.5 引用的本质

2.6 常量引用

3、函数提高

3.1 函数默认参数

3.2 函数占位参数

3.3 函数重载

3.3.1 函数重载概述

3.3.2 函数重载注意事项


1、内存分区模型

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

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

  • 全局区:存放全局变量静态变量以及常量

  • 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量

  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收

内存四区意义:

不同区域存放的数据,赋予不同的生命周期, 给我们更大的灵活编程

1.1 程序运行前

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

代码区:

存放 CPU 执行的机器指令

代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可

代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令

全局区:

全局变量和静态变量存放在此.

全局区还包含了常量区, 字符串常量其他常量也存放在此.

==该区域的数据在程序结束后由操作系统释放==.

示例:

//全局变量
int g_a = 10;
int g_b = 10;
​
//全局常量
const int c_g_a = 10;
const int c_g_b = 10;
​
int main() {
​
    //局部变量
    int a = 10;
    int b = 10;
​
    //打印地址
    cout << "局部变量a地址为: " << (int)&a << endl;
    cout << "局部变量b地址为: " << (int)&b << endl;
​
    cout << "全局变量g_a地址为: " <<  (int)&g_a << endl;
    cout << "全局变量g_b地址为: " <<  (int)&g_b << endl;
​
    //静态变量
    static int s_a = 10;
    static int s_b = 10;
​
    cout << "静态变量s_a地址为: " << (int)&s_a << endl;
    cout << "静态变量s_b地址为: " << (int)&s_b << endl;
    
    
    //字符串常量
    cout << "字符串常量地址为: " << (int)&"hello world" << endl;
    cout << "字符串常量地址为: " << (int)&"hello world1" << endl;
​
    cout << "全局常量c_g_a地址为: " << (int)&c_g_a << endl;
    cout << "全局常量c_g_b地址为: " << (int)&c_g_b << endl;
    
    //const修饰的常量
    const int c_l_a = 10;
    const int c_l_b = 10;
    cout << "局部常量c_l_a地址为: " << (int)&c_l_a << endl;
    cout << "局部常量c_l_b地址为: " << (int)&c_l_b << endl;
​
    system("pause");
​
    return 0;
}

打印结果:

总结:

  • C++中在程序运行前分为全局区和代码区

  • 代码区特点是共享和只读

  • 全局区中存放全局变量、静态变量、常量

  • 常量区中存放 const修饰的全局常量 和 字符串常量

1.2 程序运行后

栈区:

由编译器自动分配释放, 存放函数的参数值,局部变量

注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放

示例:

int * func()
{
    int a = 10;//栈区开辟的数据由编译器自动释放
    return &a;
}
​
int main() {
​
    int *p = func();
​
    cout << *p << endl;//10     第一次可以打印是编译器做了保留
    cout << *p << endl;//乱码     第二次这个数据就不再保留了
​
    system("pause");
​
    return 0;
}
​

堆区:

由程序员分配释放,若程序员不释放,程序结束时由操作系统回收

在C++中主要利用new在堆区开辟内存

示例:

int* func()
{
    int* a = new int(10);
    return a;
}
​
int main() {
​
    int *p = func();
​
    cout << *p << endl;
    cout << *p << endl;
    
    system("pause");
​
    return 0;
}

总结:

堆区数据由程序员管理开辟和释放

堆区数据利用new关键字进行开辟内存

1.3 new操作符

C++中利用new操作符在堆区开辟数据

堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符 ==delete==

语法:new 数据类型

利用new创建的数据,会返回该数据对应的类型的指针

示例1: 基本语法

int* func()
{
    int* a = new int(10);
    return a;
}
​
int main() {
​
    int *p = func();
​
    cout << *p << endl;
    cout << *p << endl;
​
    //利用delete释放堆区数据
    delete p;
​
    //cout << *p << endl; //报错,释放的空间不可访问
​
    system("pause");
​
    return 0;
}

示例2:开辟数组

//堆区开辟数组
int main() {
​
    int* arr = new int[10];
​
    for (int i = 0; i < 10; i++)
    {
        arr[i] = i + 100;
    }
​
    for (int i = 0; i < 10; i++)
    {
        cout << arr[i] << endl;
    }
    //释放数组 delete 后加 []
    delete[] arr;
​
    system("pause");
​
    return 0;
}
 

2、引用

2.1 引用的基本使用

作用: 给变量起别名

语法: 数据类型 &别名 = 原名

示例:

int main() {
​
    int a = 10;
    int &b = a;
​
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
​
    b = 100;
​
    cout << "a = " << a << endl;
    cout << "b = " << b << endl;
​
    system("pause");
​
    return 0;
}

2.2 引用注意事项

  • 引用必须初始化

  • 引用在初始化后,不可以改变

示例:

int main() {
​
    int a = 10;
    int b = 20;
    //int &c; //错误,引用必须初始化
    int &c = a; //一旦初始化后,就不可以更改
    c = b; //这是赋值操作,不是更改引用
​
    cout << "a = " << a << endl;//20
    cout << "b = " << b << endl;//20
    cout << "c = " << c << endl;//20
​
    system("pause");
​
    return 0;
}

2.3 引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参

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

示例:

//1. 值传递
void mySwap01(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
}
​
//2. 地址传递
void mySwap02(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}
​
//3. 引用传递
void mySwap03(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
    //相当于起别名
    //int &a = a;
    //int &b = b;
    //所以实参和形参是一样的
}
​
int main() {
​
    int a = 10;
    int b = 20;
​
    mySwap01(a, b);
    cout << "a:" << a << " b:" << b << endl;
​
    mySwap02(&a, &b);
    cout << "a:" << a << " b:" << b << endl;
​
    mySwap03(a, b);
    cout << "a:" << a << " b:" << b << endl;
​
    system("pause");
​
    return 0;
}
​

总结:通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单

//相当于起别名
//int &a = a;
//int &b = b;
//所以实参和形参是一样的

2.4 引用做函数返回值

作用:引用是可以作为函数的返回值存在的

注意:不要返回局部变量引用

用法:函数调用作为左值

示例:

//返回局部变量引用
int& test01() {
    int a = 10; //局部变量      栈区:函数体结束释放
    return a;
}
​
//返回静态变量引用
int& test02() {
    static int a = 20;//静态变量        全局区:程序结束后系统释放
    return a;
}
​
int main() {
​
    //不能返回局部变量的引用
    int& ref = test01();
    cout << "ref = " << ref << endl;//第一次结果正确,编译器做了保留
    cout << "ref = " << ref << endl;//第二次结果错误,因为a的内存已经释放
​
    //如果函数做左值,那么必须返回引用
    int& ref2 = test02();
    cout << "ref2 = " << ref2 << endl;//20
    cout << "ref2 = " << ref2 << endl;//20
​
    test02() = 1000;//如果函数的返回值是个引用,这个函数调用可以作为左值
    //相当于a的引用test02()等于1000
​
    cout << "ref2 = " << ref2 << endl;//1000
    cout << "ref2 = " << ref2 << endl;//1000
​
    system("pause");
​
    return 0;
}

2.5 引用的本质

本质:引用的本质在c++内部实现是一个指针常量.

讲解示例:

//发现是引用,转换为 int* const ref = &a;
void func(int& ref){
    ref = 100; // ref是引用,转换为*ref = 100
}
int main(){
    int a = 10;
    
    //自动转换为 int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改
    int& ref = a; 
    ref = 20; //内部发现ref是引用,自动帮我们转换为: *ref = 20;
    
    cout << "a:" << a << endl;
    cout << "ref:" << ref << endl;
    
    func(a);
    return 0;
}

结论:C++推荐用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针操作编译器都帮我们做了

2.6 常量引用

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

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

示例:

//引用使用的场景,通常用来修饰形参
void showValue(const int& v) {
    //v += 10;
    cout << v << endl;
}
​
int main() {
​
    //int& ref = 10;  引用本身需要一个合法的内存空间,因此这行错误
    //加入const就可以了,编译器优化代码,int temp = 10; const int& ref = temp;
    const int& ref = 10;
​
    //ref = 100;  //加入const后不可以修改变量
    cout << ref << endl;
​
    //函数中利用常量引用防止误操作修改实参
    int a = 10;
    showValue(a);
​
    system("pause");
​
    return 0;
}


3、函数提高

3.1 函数默认参数

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

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

注意:

1)如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值

2)如果函数声明有默认值,函数实现的时候就不能有默认参数,声明和实现只能有一个有默认参数

示例:

int func(int a, int b = 10, int c = 10) {
    return a + b + c;
}
​
​
int func2(int a = 10, int b = 10);
int func2(int a, int b) {
    return a + b;
}
​
int main() {
​
    cout << "ret = " << func(20, 20) << endl;
    cout << "ret = " << func(100) << endl;
​
    system("pause");
​
    return 0;
}

3.2 函数占位参数

C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置

语法: 返回值类型 函数名 (数据类型){}

占位参数也可以有默认值

在现阶段函数的占位参数存在意义不大,但是后面的课程中会用到该技术

示例:

//函数占位参数 ,占位参数也可以有默认参数
void func(int a, int = 10) {
    cout << "this is func" << endl;
}
​
int main() {
​
    func(10,10); //占位参数必须填补
​
    system("pause");
​
    return 0;
}

3.3 函数重载

3.3.1 函数重载概述

作用:函数名可以相同,提高复用性

函数重载满足条件:

  • 同一个作用域下

  • 函数名称相同

  • 函数参数类型不同 或者 个数不同 或者 顺序不同

注意: 函数的返回值不可以作为函数重载的条件

示例:

//函数重载需要函数都在同一个作用域下
void func()
{
    cout << "func 的调用!" << endl;
}
void func(int a)
{
    cout << "func (int a) 的调用!" << endl;
}
void func(double a)
{
    cout << "func (double a)的调用!" << endl;
}
void func(int a ,double b)
{
    cout << "func (int a ,double b) 的调用!" << endl;
}
void func(double a ,int b)
{
    cout << "func (double a ,int b)的调用!" << endl;
}
​
//函数返回值不可以作为函数重载条件
//int func(double a, int b)
//{
//  cout << "func (double a ,int b)的调用!" << endl;
//}
​
​
int main() {
​
    func();
    func(10);
    func(3.14);
    func(10,3.14);
    func(3.14 , 10);
    
    system("pause");
​
    return 0;
}

3.3.2 函数重载注意事项

  • 引用作为重载条件

  • 函数重载碰到函数默认参数

示例:

//函数重载注意事项
//1、引用作为重载条件
​
void func(int &a)//传入变量
{
    cout << "func (int &a) 调用 " << endl;
}
​
void func(const int &a)//传入常量、变量(重载时优先选择上面的函数)
{
    cout << "func (const int &a) 调用 " << endl;
}
​
​
//2、函数重载碰到函数默认参数
​
void func2(int a, int b = 10)
{
    cout << "func2(int a, int b = 10) 调用" << endl;
}
​
void func2(int a)
{
    cout << "func2(int a) 调用" << endl;
}
​
int main() {
    
    int a = 10;
    func(a); //调用无const【a是一个变量意思为可变】
    
    
    func(10);//调用有const【10是一个】
    //如果调用有const,则 int &a = 10 不合法
    //如果调用有const,则 const int &a = 10 合法     相当于:int temp = 10; const int& a = temp;
​
    
    
    //func2(10); //碰到默认参数产生歧义,需要避免
​
    system("pause");
​
    return 0;
}

文章图片、文字来源于 黑马程序员 ,加作增添更改,如有侵权,联系删除

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖虎不秃头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值