C++学习 day--20 new和delete关键字

news2024/11/17 21:21:42

1、new delete 基本语法

1 )在软件项目开发过程中,我们经常需要动态地分配和撤销内存空间,特别是数据结构中结点的插入与删除。在 C 语言中是利用库函数 malloc free 来分配和撤销内存空间的。C++ 提供了较简便而功能较强的运算符 new delete 来取代 malloc free 函数。
( 注意: new delete 是运算符,但也是C++里边的关键字,但不是函数,因此执行效率高。 )
2 )虽然为了与 C 语言兼容, C++ 仍保留 malloc free 函数,但建议用户不用 malloc free 函数,而用 new delete 运算符。 new 运算符的例子
new int; // 开辟一个存放整数的存储空间,返回一个指向该存储空间的地址 ( 即指针 )
new int(10); // 开辟一个存放整数的空间,并指定该整数的初值为 10 ,返回一个指向该存储空间的地址
new char[100]; // 开辟一个存放字符数组 ( 包括 100 个元素 ) 的空间,返回首元素的地址
new int[5][4]; // 开辟一个存放二维整型数组 ( 大小为 5*4) 的空间,返回首元素的地址
float *p=new float (3.14159); // 开辟一个存放单精度数的空间,并指定该实数的初值为3.14159 ,将返回的该空间的地址赋给指针变量 p
3 new delete 运算符使用的一般格式为:
new 运算符动态分配堆内存
使用方法:
普通变量:
指针变量 = new 类型 ( 常量) ;
指针变量 = new 类型 [ 表达式 ];
数组:
指针变量 = new 类型 [ 表达式 ][ 表达式 ] // 二维数组
作用:从堆上分配一块“类型”指定大小的存储空间,返回首地址
其中:“常量”是初始化值,可缺省
创建数组对象时,不能为对象指定初始值
delete 运算符释放已分配的内存空间
使用方式:
普通类型(非数组)使用 : delete 指针变量
数组使用 :
delete[] 指针变量
其中“ 指针变量 ” 必须是一个 new 返回的指针或者malloc返回的指针!
我们几个代码:
#include <stdlib.h>
#include <iostream>
using namespace std;
//分配基础类型
int main(void) {
	int* p1 = new int;//第一种分配动态内存不执行初始化
	*p1 = 100;
	int* p2 = new int(100);//第二种分配动态内存同时执行初始化
	int* p3 = (int*)malloc(sizeof(int));// 第三种 malloc 返回值是 void *
	free(p1); //基础类型可以 new h和 free 可以混搭
	delete p3; //基础类型可以 malloc delete 可以混搭
	delete p2; //free(p2); 同样效果
	system("pause");
	return 0;
}

正常编译正常运行,就是对于申请普通变量时,free和new可以混搭,及new申请的普通变量可以通过free释放。delete和malloc也可以混搭。

#include <stdlib.h>
#include <iostream>
//分配数组变量
int main(void) {
	int* p1 = (int*)malloc(sizeof(int) * 10);
	int* p2 = new int[10];
	delete p1; // 等价于free(p1); 可以混搭
	free(p2);//等价于delete p2; //可以混搭
	system("pause");
	return 0;
}

正常运行正常编译。申请连续空间时(就是数组),也能有free和new混搭使用,delete和malloc函数混搭使用。

2、 C++程序员的噩梦-内存泄漏

内存泄漏 Memory Leak - 是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
在这里说一下,内存泄漏是真的噩梦,我当时用64G 的内存的高配置电脑,但是就是用代码执行不断申请内存但不释放,结果最后进程卡死,而且整个系统都卡死了,连关机什么的都不能操作了,最后只能强行断电才让电脑重获新生。感兴趣的小伙伴可以试一下。
来看一个只申请不释放内存的例子:
#include <stdlib.h>
#include <iostream>
#include <stdio.h>
#include <Windows.h>
using namespace std;
void A_live() {
	int* p = new int[1024];
	p[0] = 0;
	//挥霍、没有释放申请的内存,申请的内存必须要“还”给系统
}

int main(void) {
	for(int i=0; i<100000; i++){
	A_live();
	Sleep(50);
	}
	return 0;
}

 感兴趣的可以运行试一下,运行一段时间,内存申请完以后,电脑没有内存,会卡死。只能强行断电再重新启动,不要轻易尝试。

正确的做法是申请用完以后记得释放,还给系统。

#include <stdlib.h>
#include <iostream>
#include <stdio.h>
#include <Windows.h>
void B_live() {
	int* p = new int[1024];//正常的开支
	p[0] = 0;
	delete[] p;//用完就还给系统
}
int main() {
	for (int i = 0; i < 100000; i++) {
		B_live();
	}
	system("pause");
	return 0;
}

 3、 变量的 4 种存储类型

所有的数据都有两种类型
数据类型: 如 int,float 等
存储类型: 总共有四种存储类型的变量,分别为自动变量(auto)、静态变量 static)、外部变量(extern)以及寄存器变量(register)。
auto - 函数中所有的非静态局部变量。
register - 一般经常被使用的的变量(如某一变量需要计算几千次)可以设置成寄存器变量,register 变量会被存储在寄存器中,计算速度远快于存在内存中的非 register 变量。
static - 在变量前加上 static 关键字的变量。之前已经讲过这个关键字了
extern - 把全局变量在其他源文件中声明成 extern 变量,可以扩展该全局变量的作用域至声明的那个文件,其本质作用就是对全局变量作用域的扩展

 注意:局部变量的作用域受块{ }的限制。但是最新的C++auto关键字进行了更新,不再表示原来的意思了,在C语言里还是表示局部变量,后面C++11会补充auto关键字!!

注意:register变量不允许取地址

#include <stdlib.h>
#include <iostream>
using namespace std;
static int yahuan_xiaoli = 24;//全局静态变量


void register_demo() {
	register int j = 0;//寄存器变量
	printf("j: %d\n", j);
	//C++ 的 register 关键字已经优化,如果我们打印它的地址,它就变成了普通的 auto 变量
		for (register int i = 0; i < 1000; i++) {
				//....
		}
	printf("&j : 0x%p\n", &j);
	{
		int k = 100;
		k += j;
	}
	printf("yahuan_xiaoli: %d\n", yahuan_xiaoli);
}

int main(void) {
	int i = 0; //C 语言的 auto 不会报错,C++ auto 已经升级啦
	register_demo();
	return 0;
}

 

 虽然register规定不允许取地址,但是通过程序我们发现,其实也是可以取地址的。

#include <stdlib.h>
#include<stdio.h>
#include <iostream>
using namespace std;
//局部静态变量
void static_demo() {
	static int girl = 18;
	int yahuan = 17;
	++girl;
	++yahuan;
	printf("girl: %d yahuan: %d\n", girl, yahuan);
}

int main() {
	static_demo();
	static_demo();
	static_demo();
}

运行结果:

其实这个static关键字我们讲过很多遍了。它修饰的变量只有在程序结束后才释放它的内存空间。当它在子函数里时,当多次调用该子函数时,它只赋值一次!!

extern就是要用其他文件.c文件的变量,我们要通过extern关键字来声明,这里就不再代码演示了。

4、 变量的作用域和生存周期

块在代码中体现就是就是{ }, auto register 在实际开发中用的不多,或者说基本没有。

5、函数返回值使用指针

可以返回函数内部:动态分配内存地址、局部静态变量地址以及全局静态变量和外部变量地址
#include <iostream>
#include <stdlib.h>
using namespace std;

int* add(int x, int y)
{
	int sum = x + y;
	return &sum;
}

int main()
{
	int a = 3, b = 5;
	int* sum = NULL;
	cout << add(a, b) << endl;
	sum = add(a, b);//不能使用外部函数局部变量的地址 bad
	cout << *sum << endl;
}

 运行结果:

觉不觉得奇怪,因为我们知道子函数的临时变量的内存空间在子函数调用结束后就被释放了,我们虽然把这块内存的地址空间返回了,但是这块空间其实已经给系统不能被用了,但是程序却正常运行了,其实我们不要看运行结果,我们要看逻辑,这种做法显然不对。其实编译是出现了警告的

下面的做法是可以的: 

#include <iostream>
#include <stdlib.h>
using namespace std;
//返回动态内存分配地址
int* add1(int x, int y)
{
	int* sum = NULL;
	sum = new int;
	*sum = x + y;
	return sum;
}

int main() {
	int a = 3, b = 5;
	int* sum = NULL;
	sum = add1(a, b);//接收外部函数动态内存分配的地址 ok
	cout << *sum << endl;
	delete sum;//用完要释放
}

运行结果:

因为堆区要手动 释放,因此这种返回方式是可以的,但是别望了在外部释放,否则就会产生内存泄漏了。

下面这种方式也是可以的:

#include <iostream>
#include <stdlib.h>
using namespace std;

//返回局部静态变量的地址
int* add2(int x, int y)
{
	static int sum = 0;
	printf("sum: %d\n", sum);
	sum = x + y;
	return &sum;
}
int main() {
	int a = 3, b = 5;
	int* sum = NULL;

	sum = add2(a, b);//接收外部函数局部静态变量的地址
	cout << *sum << endl;
	*sum = 88888;
	cout << *sum << endl;
}

运行结果:

 

 6、常见的错误总结:

1. 申请的内存 多次释放 ,这是容易犯错的一个问题, 多次释放程序会出现异常,会挂起
2. 内存泄漏, 只申请,不释放
3. 释放的内存不是申请时的地址 ,这个问题最大,我们释放的地址只能是从堆区申请的空间,如果释放栈区或其他区的地址,会产生一些列意想不到的问题。
4. 释放空指针
5. 释放一个内存块,但继续引用其中的内容 ,释放后的空间是不能再次访问的!!
6. 越界访问
我们通过代码来看看这几种错误:

第一种:多次释放

#include <iostream>
#include <stdlib.h>
using namespace std;
int main()
{
	int* p = new int[18];
	p[0] = 0;
	delete[] p;//只允许一次释放
	delete[] p;//1.申请的内存多次释放, 程序出现异常
}

 这是一种典型错误,虽然编译能通过,但是是违法的,我们以后在判断程序对不对时不能仅仅只看是否通过编译和运行,因为编译器各有差别,我们要从根源上找原因,因此我们需要扎实的基础功底。

第二种错误:

#include <iostream>
#include <stdlib.h>
using namespace std;

int main() {
	char* p1 = NULL;
   
	do{
		p1 = new char[10]; //2. 忘记 delete,内存泄漏
	}while(1==1);
}

这种也是典型的错误,特别是在子函数里申请内存时,在外部函数最容易忘记释放,从而导致内存泄漏。 

第三种错误:释放的内存不是自己手动申请的内存

#include <iostream>
#include <stdlib.h>
using namespace std;

int main() {
   //3.释放的内存不是申请时的地址
	char a[10] = "1234567";
	char* p = a;
	for(int i=0; i<sizeof(a)/sizeof(a[0]); i++){
	cout<<*(p++)<<endl;
	}
	delete [] p;
}

运行结果:直接终止程序了。所以释放只能释放自己手动申请的内存。

 第四种错误:释放空指针

#include <iostream>
#include <stdlib.h>
using namespace std;

int main() {
	//4.释放空指针
	char* p1 = NULL;
	if (1 == 0) { //比如文件能打开的情况
		p1 = new char[2048];
	}
	delete p1;
}

这种错误能通过编译也能通过运行,但是空指针是指向值为0(NULL)的指针,这个指针不需要释放!!

第五种错误:释放后再次引用该内存的内容

#include <iostream>
#include <stdlib.h>
using namespace std;
int main() {
	//5.释放一个内存块,但继续引用其中的内容
	char a[] = "1234";
	char* p = a;
    delete[] p;
    p[0]= '\0';//绝对禁止
}

运行结果:直接给你终止程序。

第六种错误:

#include <iostream>
#include <stdlib.h>
using namespace std;
int main() {
	//6.越界访问
    int* p = new int[3];
    memset(p, 0, 3 * sizeof(int));
    for (int i = 0; i < 3; i++) {
	cout << *(p++) << endl;
    }
   //误判
    for (int i = 0; i < 3; i++) {
	  cout << *(p++) << endl;
     }
     cout << "come here!" << endl;
}

 运行结果:

运行结果:

因为第一个循环指针已经指向最后一个元素了,此时在往后移动,那么就越界访问了。这种错误也是常见错误,而且还不容易被发现。

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

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

相关文章

Screenium for Mac: 重新定义屏幕录制体验

你是否曾经需要记录你的屏幕活动&#xff0c;但却被复杂的软件界面和功能缺失所困扰&#xff1f;现在&#xff0c;Screenium for Mac可以解决你的烦恼。这是一款全新的屏幕录制工具&#xff0c;以其强大的功能和易用性&#xff0c;让你的屏幕录制体验焕然一新。 Screenium for…

三十七、【进阶】验证索引的效率

1、准备工作&#xff1a; 创建一张表&#xff0c;该表中有一千万条数据&#xff0c;名为tb_sku&#xff1b; 2、使用主键查询&#xff1a; select * from tb_stu where id1\G; 3、使用非索引查询&#xff1a; 4、给sn字段创建索引&#xff1a; 在创建过程中&#xff0c;发现…

工业4.0的安全挑战与解决方案

在当今数字化时代&#xff0c;工业4.0已经成为制造业的核心趋势。工业4.0的兴起为生产企业带来了前所未有的效率和灵活性&#xff0c;但与之伴随而来的是一系列的安全挑战。本文将深入探讨工业4.0的安全挑战&#xff0c;并提供一些解决方案&#xff0c;以确保制造业的数字化转型…

FOC系列(二)----继续学习DRV8301芯片

一、 程序框图 跟随上篇博客咱们继续往下看&#xff0c;下面是芯片内部的程序框图&#xff1a; 1.1 BUCK电路 1.2 内部各电源 1.3 SPI通信、栅极驱动器和时序控制器 1.4 MOSFET驱动电路 1.5 电流采样放大电路 数据手册只是给出了这一部分框图&#xff0c;但是没有更加详细的介…

解锁高效检索技能:掌握MySQL索引数据结构的精髓

文章目录 磁盘存储假设每条sql信息为1kb&#xff0c;主键ID为bigint型&#xff0c;一颗高度为2&#xff0c;3&#xff0c;4高度的B树分别可以存储多少行数据?为什么选用B树做索引而不选用二叉树或者B树?1.减少IO次数2.稳定查询3.存储效率高 为什么用 B 树做索引而不用哈希表做…

99. 激光炸弹(二维前缀和)

题目&#xff1a; 99. 激光炸弹 - AcWing题库 思路&#xff1a; 1.矩形/正方形求最值--->二维前缀和 2.注意&#xff1a;此题不可开两个数组&#xff0c;空间会爆&#xff0c;前缀和数组与原数据数组共用一个数组。 代码&#xff1a; #include <cstring> #inc…

如何保护您的个人信息数据库安全?

保护个人信息数据库安全是一项至关重要的任务&#xff0c;因为我们的个人信息可能包含许多敏感数据&#xff0c;如果泄露可能会导致各种问题&#xff0c;例如身份盗窃、网络诈骗等。以下是一些建议&#xff0c;可以帮助您保护您的个人信息数据库安全&#xff1a; 加密数据&…

Redis进军磁盘存储

目录 1、对抗价格优势&#xff1a;纳入磁盘&#xff0c;降低成本&#xff1f; 2、Redis的野心&#xff1a;无敌是多么寂寞&#xff0c;所以我们要开新地图 3、开发者异议&#xff1a;他们正在偏离我们选择Redis的初衷 4、结语&#xff1a;性能为王&#xff0c;但绝不甘于只…

公网远程访问macOS本地web服务器

# 公网访问macOS本地web服务器【内网穿透】 文章目录 1. 启动Apache服务器2. 公网访问本地web服务2.1 本地安装配置cpolar2.2 创建隧道2.3 测试访问公网地址3. 配置固定二级子域名3.1 保留一个二级子域名3.2 配置二级子域名4. 测试访问公网固定二级子域名 以macOS自带的Apache…

mac 查看GPU使用

首先搜索活动监视器 然后 点击窗口->gpu历史记录 记住不是立马出结果&#xff0c;而是 需要等半分钟左右的

Project Costs

/*** 初始化象棋的棋子&#xff0c;正常情况加载双方所有棋子&#xff0c;残局演示加载剩余棋子&#xff0c;按坐标位置摆放* * 【费用】* 因甲方要求产生工作量计算费用&#xff1b;新增、修改、删除需求* 因乙方生产缺陷工作量不计费用&#xff1b;缺陷、延误* * 来说个一个栗…

Istio 运行错误 failed to update resource with server-side apply for obj 问题解决

Istio 环境 kubernetes version: v1.18.2 istio version: v1.10.0运行之后 istio-operator 的日志就抛出下面错误&#xff0c;而且会一直重启 # kubectl get iop -A NAMESPACE NAME REVISION STATUS AGE istio-system iop-pro-cluster…

Helplook VS Document360:哪个更适合知识库管理?

在当今快速发展的业务环境中&#xff0c;高效的知识库管理对于组织有效地组织和监督其在线文档至关重要。Document360和HelpLook是两个强大的软件解决方案&#xff0c;在这一领域表现出色。在这篇博文中&#xff0c;我们将比较 Document360 和 HelpLook&#xff0c;对它们的特性…

操作系统缓冲区管理(单缓冲,双缓冲,循环缓冲,缓冲池)

目录 1.什么是缓冲区1.有什么作用 2.单缓冲1.常考题型:计算每处理一块数据平均需要多久1.假设输入时间T大于处理时间C2.假设输入时间T小于处理时间C 2.单缓冲通信 3.双缓冲1.计算数据处理的时间1. 假设T>CM2.假设T<CM 2.双缓冲通信 4.循环缓冲5.缓冲池1.共用缓冲区2.工作…

java版直播商城平台规划及常见的营销模式 电商源码/小程序/三级分销+商城免费搭建

涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis …

第十三章---枚举类型与泛型

一&#xff0c;枚举类型 1.使用枚举类型设置常量 设置常量时&#xff0c;我们通常将常量放置在接口中&#xff0c;这样在程序中就可以直接使用。该常量稚因为在接口中定义常量时&#xff0c;该常量的修饰符为 final 与 static。 public interface Constants ( public static …

C#两个表多条件关联写法

文章目录 C#两个表多条件关联写法两个表实体类准备实体类数据初始化第一种 sql的左关联第二种相当于sql的 INNER JOIN写法一&#xff1a;FROM a FROM b where 多条件关联写法二&#xff1a; FROM JOIN INTO写法三&#xff1a; FROM JOIN 省略into C#两个表多条件关联写法 两个…

order by数据过多引起的cpu飙升

测试环境 1.目前数据库类型为pg数据库2.目前数据库业务为共享数据库,为减少其他业务对本次测试的影响,故选在业务空闲时间执行3.服务器性能为8C 32GB 500GB硬盘 原程序测试结果 优化后程序结果 出现原因 当数据量大时&#xff0c;order by排序操作会消耗大量的CPU资源&#…

Android登录注册页面(第三次作业)

第三次作业 - 登录注册页面 题目要求 嵌套布局。使用线性布局的嵌套结构&#xff0c;实现登录注册的页面。&#xff08;例4-3&#xff09; 创建空的Activity 项目结构树如下图所示&#xff1a; 注意&#xff1a;MainActivity.java文件并为有任何操作&#xff0c;主要功能集中…

Python---continue关键字对while...else结构的影响(只是跳过某次,之后继续正常结束,执行else)

回顾&#xff1a; 在Python循环中&#xff0c;经常会遇到两个常见的关键词&#xff1a;break 与 continue break&#xff1a;代表终止整个循环结构 continue&#xff1a;代表中止当前本次循环&#xff0c;继续下一次循环 break&#xff1a; 英 /breɪk/ v. 打破&#xff…