C++基础精讲-02

news2025/4/18 23:07:26

文章目录

  • 1.C/C++申请、释放堆空间的方式对比
    • 1.1C语言申请、释放堆空间
    • 1.2C++申请、释放堆空间
      • 1.2.1 new表达式申请数组空间
    • 1.3回收空间时的注意事项
    • 1.4malloc/free 和 new/delete 的区别
  • 2.引用
    • 2.1 引用的概念
    • 2.2 引用的本质
    • 2.3 引用与指针的联系与区别
    • 2.4 引用的使用场景
      • 2.4.1 引用作为函数的参数
    • 2.5 引用作为函数的返回值
    • 2.6 总结
  • 3.强制类型转换
    • 3.1 c语言里的强制类型转换
    • 3.2 c++数据类型强制转换
      • 3.2.1 static_cast(常用)
      • 3.2.3 const_cast (常不用)
      • 3.2.3 其他(后面章节中应用到在讲解)


1.C/C++申请、释放堆空间的方式对比

1.1C语言申请、释放堆空间

关于C语言的堆内存管理内存管理不熟悉可看以下章节
C语言精讲-12
c语言中申请、释放堆空间一般用malloc和free函数

#include<stdio.h>
#include<stdlib.h>

void test(void)
{
        //申请内存
        int*p=(int*) malloc(sizeof(int));
        if(NULL==p)
        {
                printf("内存申请失败\n");
                exit(0) ;
        }
        *p=10;
        printf("*p=%d\n",*p);

        //释放内存;
        free(p);
        p=NULL;

}

int main(void)
{
        test();
        return 0;

}

在这里插入图片描述

1.2C++申请、释放堆空间

c++申请、释放堆空间一般用new/delete表达式;

#include<iostream>
using std::cout;
using std::endl;

void test(void)
{
        //初始化为该类型的默认值
        int * p1 = new int();
        cout<<*p1<<endl;

        int *p2=new int(2);
        cout<<*p2<<endl;

        //释放内存
        delete p1;
        delete p2;
        //将 p1 和 p2 置为 nullptr;避免野指针
        p1= nullptr;
        p2= nullptr;

}

int main(void)
{
        test();
        return 0;

}

1.2.1 new表达式申请数组空间

1.默认初始化为0;

#include<iostream>
using std::cout;
using std::cin;
using std::endl;

void test(void)
{
        //将动态分配的数组元素默认初始化为 0
        //申请10个int类型的堆空间
        int * p1 = new int[10]();
        for(int idx = 0; idx < 10; ++idx)
        {
        p1[idx] = idx;
        }

        for(int idx = 0; idx < 10; ++idx)
        {
        cout << p1[idx] << endl;
        }
        //释放堆空间
        delete [] p1;
        p1=nullptr;


}

int main(void)
{       
        test();
        return 0;
}       
~                                       

在这里插入图片描述
2.赋初值

#include<iostream>
using std::cout;
using std::cin;
using std::endl;

void test(void)
{       
        //申请3个int类型的堆空间
        int * p1 = new int[3]{2,4,6};
        for(int i=0;i<3;i++)
        {
                cout<<p1[i]<<endl;
        }
        //释放堆空间
        delete [] p1;
        p1=nullptr;


}

int main(void)
{       
        test();
        return 0;
}       
~                           

在这里插入图片描述

1.3回收空间时的注意事项

1.三组申请空间和回收空间的匹配组合

malloc            free
    
new               delete
    
new int[5]()      delete[]

2安全回收
delete只是回收了指针指向的空间,但这个指针变量依然还在,指向了不确定的内容(野指针),容易造成错误。所以需要进行安全回收,将这个指针设为空指针C++11之后使用nullptr表示空指针。

1.4malloc/free 和 new/delete 的区别

1.malloc/free 是库函数;new/delete 是表达式,后两者使用时不是函数的写法;
2.new 表达式的返回值是相应类型的指针,malloc 返回值是 void*;
3.malloc 申请的空间不会进行初始化,获取到的空间是有脏数据的,但 new 表达式申请空间时可以直接初始化;
4.malloc 的参数是字节数,new 表达式不需要传递字节数,会根据相应类型自动获取空间大小。

2.引用

2.1 引用的概念

引用是c++对c的重要扩充。在c/c++中指针的作用基本都是一样的,但是c++增加了另外一种给函数传递地址的途径,这就是按引用传递
变量概述
1.变量名实质上是一段连续内存空间的别名,是一个标号(门牌号)
2.程序中通过变量来申请并命名内存空间
3.通过变量的名字可以使用存储空间
c++中新增了引用的概念,引用可以作为一个已定义变量的别名。
基本语法:

Type& ref = val;
示例:
int number = 2;
int & ref = number;
#include<iostream>
using std::cout;
using std::cin;
using std::endl;


int main(void)
{

        int sum=10;
        cout<<"sum="<<sum<<endl;
        int&s=sum;
        cout<<"s="<<s<<endl;
        s=100;
        cout<<"s="<<s<<endl<<"num="<<sum<<endl;

        return 0;
}
                                            

在这里插入图片描述
注意事项
&在此不是求地址运算,而是起标识作用。
类型标识符是指目标变量的类型
必须在声明引用变量时进行初始化。
引用初始化之后不能改变
不能有NULL引用。必须确保引用是和一块合法的存储单元关联。

2.2 引用的本质

引用的本质在c++内部实现是一个常指针;c++编译器在编译过程中使用常指针作为引用的内部实现,因此引用所占用的空间大小与指针相同,只是这个过程是编译器内部实现,用户不可见。

#include<iostream>
using std::cout;
using std::cin;
using std::endl;

void test(void)
{
        int sum=10;
        int &s=sum;
        int*p=&sum;
        cout<<"sum的地址:"<<&sum<<endl;
        cout<<"s的地址:"<<&s<<endl;
        cout<<"p所指向的地址:"<<p<<endl;

        cout<<"size(p)="<<sizeof(p)<<endl;//指针p占多少字节
        cout<<"size(s)="<<sizeof(&s)<<endl;//引用&s占多数字节
}

int main(void)
{

        test();


        return 0;
}

                        

在这里插入图片描述

2.3 引用与指针的联系与区别

联系:

  1. 引用和指针都有地址的概念,都是用来间接访问变量;

  2. 引用的底层还是指针来完成,可以把引用视为一个常指针。

区别
4. 引用必须初始化,指针可以不初始化;
5. 引用不能修改绑定,但是指针可以修改指向;
6. 在代码层面对引用本身取址取到的是变量本体的地址,但是对指针取址取到的是指针变量的地址

2.4 引用的使用场景

2.4.1 引用作为函数的参数

在没有引用之前,如果我们想通过形参改变实参的值,只有使用指针才能到达目的。但使用指针的过程中,不好操作,很容易犯错。 而引用既然可以作为其他变量的别人而存在,那在很多场合下就可以用引用代替指针,因而也具有更好的可读性和实用性。这就是引用存在的意义。

#include<iostream>
using std::cout;
using std::cin;
using std::endl;

//使用指针
void swap(int *a,int*b)
{
        int temp=*a;
        *a=*b;
        *b=temp;
}
//使用引用
void swap2(int&a,int&b)
{
        int temp=a;
        a=b;
        b=temp;
}

void test1(void)
{
        int a=10,b=20;
        cout<<"交换前:a="<<a<<",b="<<b<<endl;
        swap(&a,&b);
        cout<<"交换后:a="<<a<<",b="<<b<<endl;
        swap2(a,b);
        cout<<"再次交换后:a="<<a<<"b="<<b<<endl;
}
int main(void)
{
        test1();
        return 0;
}


在这里插入图片描述

2.5 引用作为函数的返回值

当以引用作为函数的返回值时,返回的变量其生命周期一定是要大于函数的生命周期的,即当函数执行完毕时,返回的变量还存在。
目的: 避免复制,节省开销

#include<iostream>
using std::cout;
using std::cin;
using std::endl;

int fun1(void)
{
        int a=10;

        //返回的是a的副本,变量a是局部变量。在fun1函数结束时a的空间就被回收
        cout<<"fun a的地址:"<<&a<<endl;
        return a;
}

int& fun2(void)
{
        ///使用指针/引用作为返回值时候,不可返回一个局部的变量;可加static修饰
        static int  b=100;

        //返回的是一个绑定b的的引用;
        cout<<"fun2 b的地址:"<<&b<<endl;
        return b;
}

int main(void)
{
        //把fun1中a的值复制给main函数中的变量a;
        int a=fun1();
        cout<<"main中a的地址:"<<&a<<endl;

        int &b=fun2();
        cout<<"main 中引用b的地址:"<<&b<<endl;

        return 0;
}

在这里插入图片描述
注意事项
1.不要返回局部变量的引用。因为局部变量会在函数返回后被销毁,被返回的引用就成为了"无所指"的引用,程序会进入未知状态。
2.不要轻易返回一个堆空间变量的引用,非常容易造成内存泄漏。

int & func()
{
	int * pint = new int(1);
	return *pint;
}

void test()
{
	int a = 2, b = 4;
	int c = a + func() + b;//内存泄漏
}

2.6 总结

  1. 在引用的使用中,单纯给某个变量取个别名没有什么意义,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不理想的问题。
  2. 用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,还可以通过const的使用,保证了引用传递的安全性。
  3. 引用与指针的区别是,指针通过某个指针变量指向一个变量后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;引用底层仍然是指针,但是编译器不允许访问到这个底层的指针,逻辑上简单理解为——对引用的操作就是对目标变量的操作。可以用指针或引用解决的问题,更推荐使用引用

3.强制类型转换

3.1 c语言里的强制类型转换

格式:数据类型 变量1 = (数据类型)变量2;

#include<stdio.h>

int main(void)
{       
        float a=3.14f;

        printf("a=%f\n",a);
        int b=(int)a;
        printf("b=%d\n",b);
        
        return 0;
}       

缺点:

  1. 数据丢失风险高
    C 语言的强制类型转换语法简单,不过在将大类型转换为小类型时,容易发生数据丢失。比如把long类型转换为int类型,若long类型的值超出int类型的范围,就会出现数据截断。
  2. 破坏类型系统且缺乏安全性检查
    C 语言允许进行各种类型的强制转换,像指针类型的转换,这可能会破坏类型系统的安全性,编译器也不会做严格的类型检查。例如将void*指针转换为其他类型的指针,若使用不当,就会引发未定义行为。
  3. 掩盖潜在错误
    C 语言的强制类型转换可能会掩盖代码中的潜在错误,使代码能通过编译,但实际上逻辑可能存在问题。
  4. 可移植性差
    不同平台上数据类型的大小和表示方式可能不同,C 语言的强制类型转换可能会导致在不同平台上产生不同的结果,影响代码的可移植性。

3.2 c++数据类型强制转换

3.2.1 static_cast(常用)

最常用的类型转换符,在正常状况下的类型转换, 用于将一种数据类型转换成另一种数据类型,如把int转换为float

目标类型 转换后的变量 = static_cast<目标类型>(要转换的变量)

好处:不允许非法的转换发生;方便查找

int a = 100float f = 0;
f = (float) a;//C风格
f = static_cast<float>(a);
void * p1 = malloc(sizeof(int));
int * p2 = static_cast<int*>(p1);
*p2 = 1;

不能完成任意两个指针类型间的转换(限制一些非法转换)

int a = 1;
int * p1 = &a;
float * p2 = static_cast<float *>(p1);//错误

总结,static_cast的用法主要有以下几种:

1)用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性需要开发人员来保证;
2)把void指针转换成目标类型的指针,但不安全;
3)把任何类型的表达式转换成void类型;
4)用于类层次结构中基类和子类之间指针或引用的转换

3.2.3 const_cast (常不用)

该运算符用来修改类型的const属性。
指向常量的指针被转化成普通指针,并且仍然指向原来的对象;
常量引用被转换成非常量引用,并且仍然指向原来的对象;

const int number = 100;
int * pInt = &number;//error
int * pInt2 = const_cast<int *>(&number);

3.2.3 其他(后面章节中应用到在讲解)

dynamic_cast:该运算符主要用于基类和派生类间的转换;
reinterpret_cast:功能强大,慎用(也称为万能转换);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2332579.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【网络安全】Linux 命令大全

未经许可,不得转载。 文章目录 前言正文文件管理文档编辑文件传输磁盘管理磁盘维护网络通讯系统管理系统设置备份压缩设备管理其它命令前言 在网络安全工作中,熟练掌握 Linux 系统中的常用命令对于日常运维、日志分析和安全排查等任务至关重要。 以下是常用命令的整理汇总,…

C++学习之ORACLE①

目录 1.ORACLE数据库简介 2..ORACLE数据库安装 3..ORACLE体系结构 4..ORACLE基本概念 5..ORACLE基本元素 6..ORACLE数据库启动和关闭 7.SQLPLUS登录ORACLE数据库相关操作 8.SQLPLUS的基本操作 9.oracle中上课使用的方案 10.SQL语言分类 11.SQL中的select语句语法和注…

企业级开发SpringBoost玩转Elasticsearch

案例 Spring Boot 提供了 spring-data-elasticsearch 模块&#xff0c;可以方便地集成 Elasticsearch。 下面我们将详细讲解如何在 Spring Boot 中使用 Elasticsearch 8&#xff0c;并提供示例代码。 1. 添加依赖: 首先&#xff0c;需要在 pom.xml 文件中添加 spring-data-e…

从零开始的图论讲解(1)——图的概念,图的存储,图的遍历与图的拓扑排序

目录 前言 图的概念 1. 顶点和边 2. 图的分类 3. 图的基本性质 图的存储 邻接矩阵存图 邻接表存图 图的基本遍历 拓扑排序 拓扑排序是如何写的呢? 1. 统计每个节点的入度 2. 构建邻接表 3. 将所有入度为 0 的节点加入队列 4. 不断弹出队头节点&#xff0c;更新其…

SpringBoot框架—启动原理

1.SpringBootApplication注解 在讲解启动原理之前先介绍一个非常重要的注解SpringBootApplication&#xff0c;这个注解在Springboot程序的入口文件Application.java中必须添加。SpringBootApplication是一个整合了三个核心注解的组合注解。 三个核心注解的作用机制&#xff1…

怎么检查网站CDN缓存是否生效

为什么要使用CDN缓存&#xff1f; 网站使用缓存可显著提升加载速度&#xff0c;减少服务器负载和带宽消耗&#xff0c;优化用户体验&#xff0c;增强架构稳定性&#xff0c;助力SEO优化&#xff0c;实现资源高效利用与性能平衡。 通过合理配置 CDN 缓存策略&#xff0c;可降低…

【自然语言处理】深度学习中文本分类实现

文本分类是NLP中最基础也是应用最广泛的任务之一&#xff0c;从无用的邮件过滤到情感分析&#xff0c;从新闻分类到智能客服&#xff0c;都离不开高效准确的文本分类技术。本文将带您全面了解文本分类的技术演进&#xff0c;从传统机器学习到深度学习&#xff0c;手把手实现一套…

vba讲excel转换为word

VBA将excel转换为word Sub ExportToWordFormatted() 声明变量Dim ws As Worksheet 用于存储当前活动的工作表Dim rng As Range 用于存储工作表的使用范围&#xff08;即所有有数据的单元格&#xff09;Dim rowCount As Long, colCount As Long 用于存储数据范围的行数和列数…

ubuntu安装openWebUI和Dify【自用详细版】

系统版本&#xff1a;ubuntu24.04LTS 显卡&#xff1a;4090 48G 前期准备 先安装好docker和docker-compose&#xff0c;可以参考我之前文章安装&#xff1a; ubuntu安装docker和docker-compose【简单详细版】 安装openWebUI 先docker下载ollama docker pull ghcr.nju.edu.c…

基于Flask的勒索病毒应急响应平台架构设计与实践

基于Flask的勒索病毒应急响应平台架构设计与实践 序言&#xff1a;安全工程师的防御视角 作为从业十年的网络安全工程师&#xff0c;我深刻理解勒索病毒防御的黄金时间法则——应急响应速度每提升1分钟&#xff0c;数据恢复成功率将提高17%。本文介绍的应急响应平台&#xff…

spark数据清洗案例:流量统计

一、项目背景 在互联网时代&#xff0c;流量数据是反映用户行为和业务状况的重要指标。通过对流量数据进行准确统计和分析&#xff0c;企业可以了解用户的访问习惯、业务的热门程度等&#xff0c;从而为决策提供有力支持。然而&#xff0c;原始的流量数据往往存在格式不规范、…

list的使用以及模拟实现

本章目标 1.list的使用 2.list的模拟实现 1.list的使用 在stl中list是一个链表,并且是一个双向带头循环链表,这种结构的链表是最优结构. 因为它的实现上也是一块线性空间,它的使用上是与string和vector类似的.但相对的因为底层物理结构上它并不像vector是线性连续的,它并没有…

【今日三题】小乐乐改数字 (模拟) / 十字爆破 (预处理+模拟) / 比那名居的桃子 (滑窗 / 前缀和)

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;每日两三题 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 小乐乐改数字 (模拟)十字爆破 (预处理模拟&#xff09;比那名居的桃子 (滑窗 / 前缀和) 小乐乐改数字 (模拟) 小乐乐改数字…

基于 Qt 的图片处理工具开发(一):拖拽加载与基础图像处理功能实现

一、引言 在桌面应用开发中&#xff0c;图片处理工具的核心挑战在于用户交互的流畅性和异常处理的健壮性。本文以 Qt为框架&#xff0c;深度解析如何实现一个支持拖拽加载、亮度调节、角度旋转的图片处理工具。通过严谨的文件格式校验、分层的架构设计和用户友好的交互逻辑&am…

44、Spring Boot 详细讲义(一)

Spring Boot 详细讲义 目录 Spring Boot 简介Spring Boot 快速入门Spring Boot 核心功能Spring Boot 技术栈与集成Spring Boot 高级主题Spring Boot 项目实战Spring Boot 最佳实践总结 一、Spring Boot 简介 1. Spring Boot 概念和核心特点 1.1、什么是 Spring Boot&#…

虽然理解git命令,但是我选择vscode插件!

文章目录 2025/3/11 补充一个项目一个窗口基本操作注意 tag合并冲突已有远程&#xff0c;新加远程仓库切换分支stash 只要了解 git 的小伙伴&#xff0c;应该都很熟悉这些指令&#xff1a; git init – 初始化git仓库git add – 把文件添加到仓库git commit – 把文件提交到仓库…

idea 打不开terminal

IDEA更新到2024.3后Terminal终端打不开的问题_idea terminal打不开-CSDN博客

【JVM】JVM调优实战

&#x1f600;大家好&#xff0c;我是白晨&#xff0c;一个不是很能熬夜&#x1f62b;&#xff0c;但是也想日更的人✈。如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下&#x1f440;白晨吧&#xff01;你的支持就是我最大的动力&#xff01;&#x1f4…

FPGA_DDR(二)

在下板的时候遇到问题 1&#xff1a;在写一包数据后再读&#xff0c;再写再读 这时候读无法读出 查看时axi_arready没有拉高 原因 &#xff1a; 由于读地址后没有拉高rready,导致数据没有读出卡死现象。 解决结果

【吾爱出品】[Windows] 鼠标或键盘可自定义可同时多按键连点工具

[Windows] 鼠标或键盘连点工具 链接&#xff1a;https://pan.xunlei.com/s/VONSFKLNpyVDeYEmOCBY3WZJA1?pwduik5# [Windows] 鼠标或键盘可自定义可同时多按键连点工具 就是个连点工具&#xff0c;功能如图所示&#xff0c;本人系统win11其他系统未做测试&#xff0c;自己玩…