C++:C/C++内存管理

C++:C/C++内存管理

    • C语言
      • C语言内存分配回顾
      • malloc & calloc & realloc & free
    • C++
      • new & delete
      • new[ ] & delete[ ]
      • 定位new
      • new & delete原理
    • malloc / free 与 new / delete对比


C语言

C语言内存分配回顾

我们先回顾一下C语言的内存分配:
在这里插入图片描述

解析:

int globalVar = 1;
在main外部,将globalVar定义在了全局,放在了C静态区


static int staticGlobalVar = 1;
在main外部,也是将staticglobalVar定义在了全局,也存储在C静态区


两者区别:
没有被static修饰的全局变量在整个程序中都是可见的,可以被其他文件访问和修改;而被static修饰的全局变量只能在定义该变量的文件内部可见,无法被其他文件访问。


static int staticVar = 1;
static修饰的局部变量会在整个程序执行过程中保留其值,直到程序结束。而staticVar也被存到的C静态区


int localVar = 1;
一个普通的局部变量定义,存储在A栈区


int num1[10] = { 1, 2, 3, 4 };
是一个数组定义过程,也是一个普通变量,被存储在A栈区


char char2[] = "abcd";
是一个数组定义过程,不过是在数组内部承载了一个字符串,此时会将字符串一个一个字符拷贝进数组中,所以最后char2是一个指针,而*char2是一个数组或者是首元素。都被存在A栈区


const char* pChar3 = "abcd";
用一个const指针接收了字符串 "abcd"的地址,对于pChar3本身,是一个指针,存储在A栈区。对于*pChar3,则是这个常量字符串 "abcd",存储在D常量区


int* ptr1 = (int*)malloc(sizeof(int) * 4);
是一个动态内存分配过程,ptr1本身是一个指针,存储在A栈区。而*ptr1则是指向一块动态内存,存放在B堆区

sizeof(num1)
num1是一个数组,此时得到的是数组的大小4*10 = 40


sizeof(char2)
char2是一个数组,内部存储着”abcd“被一个一个字符拷贝后的结果,由于字符串末尾有一个’\0‘,所以数组长度比实际长1。最后大小为1 * (4 + 1)=5


strlen(char2)
strlen用于统计字符串的长度,遇到’\0‘时停止同统计,所以结果为4


sizeof(pChar3)
pChar3是一个指针,在32位计算机中指针大小为4,64位计算机中指针大小为8。所以答案为4/8


strlen(pChar3)
pChar3指向字符串”abcd”,长度为4,结果为4


sizeof(ptr1)
ptr1是一个指针,在32位计算机中指针大小为4,64位计算机中指针大小为8。所以答案为4/8

答案:

在这里插入图片描述
在这里插入图片描述


malloc & calloc & realloc & free

在C语言中,动态内存管理主要是通过malloccallocreallocfree四个函数完成的。
我们简单回顾一下它们的作用与区别:
malloc,calloc和realloc是C语言中用于动态内存分配的函数。

  1. malloc函数:

    • 作用:malloc函数用于在程序运行时动态分配指定大小的内存空间。
    • 使用方法:malloc函数的原型为void *malloc(size_t size),其中size参数表示需要分配的内存空间大小(以字节为单位)。函数返回一个void指针,指向分配的内存空间的起始地址。
    • 示例:
      int *ptr;
      ptr = (int *)malloc(10 * sizeof(int));
      
  2. calloc函数:

    • 作用:calloc函数用于在程序运行时动态分配指定数量、指定大小的内存空间,并将分配的内存空间初始化为零。
    • 使用方法:calloc函数的原型为void *calloc(size_t num, size_t size),其中num参数表示需要分配的元素个数,size参数表示每个元素的大小(以字节为单位)。函数返回一个void指针,指向分配的内存空间的起始地址。
    • 示例:
      int *ptr;
      ptr = (int *)calloc(10, sizeof(int));
      
  3. realloc函数:

    • 作用:realloc函数用于修改之前动态分配的内存空间的大小。
    • 使用方法:realloc函数的原型为void *realloc(void *ptr, size_t size),其中ptr参数是之前由malloc或calloc分配的内存空间的指针,size参数表示新的内存空间大小。函数返回一个void指针,指向修改后的内存空间的起始地址。如果返回空指针,则表示内存分配失败。
    • 示例:
      int *ptr;
      ptr = (int *)realloc(ptr, 20 * sizeof(int));
      

三者的异同点如下:

  • malloccalloc都用于动态分配内存空间,而realloc用于调整动态分配的内存空间的大小。
  • malloccalloc都返回分配的内存空间的起始地址,而realloc返回修改后的内存空间的起始地址。
  • malloccalloc分配的内存空间不会被初始化,而realloc可能会保留之前分配的内存内容。
  • realloc函数可能会将之前分配的内存空间内容复制到新的内存空间中,所以在使用realloc时要小心,以免丢失之前分配的内存中的数据。
  • realloc函数还可以用于分配新的内存空间,如果之前的指针是空指针,则realloc的操作相当于malloc

C++

new & delete

在C++中,newdelete是用于动态分配和释放内存的关键字。动态分配内存是指在程序运行时按需分配所需的内存,而不是在编译时固定分配内存。

new关键字用于动态分配单个对象的内存,并返回指向该对象的指针。其语法如下:

pointer = new type;

其中,pointer是一个指针,用于存储分配的内存地址,type是要分配内存的对象类型。

例如,以下代码分配了一个整数的内存,并将地址存储在ptr指针中:

int* ptr = new int;

此时,ptr指向一个未初始化的整数对象。

此外,如果希望分配的内存被初始化,可以用以下语法:

pointer = new type();

比如以下代码:

int* ptr = new int(10);

就是开辟了一个int的空间,并赋值为10。


要释放动态分配的内存,可以使用delete关键字。其语法如下:

int* pointer = new int;
delete pointer;

需要注意的是,使用delete释放指针指向的内存后,该指针将不再有效,因为内存已经被释放。为了避免悬挂指针的问题,可以在释放内存后将指针设置为nullptr,以防止后续误用。

delete ptr;
ptr = nullptr;

但是到此为止,好像C++的动态内存和C语言的功能没什么区别。其实C++的new和delete与C语言的malloc和free的区别体现在类上。

当使用new关键字来创建对象时,会调用对象的构造函数,而malloc不会。
比如以下代码:

class A
{
public:
    A(int a = 0)
        : _a(a)
    {
        cout << "A():" << this << endl;
    }

    ~A()
    {
        cout << "~A():" << this << endl;
    }
private:
    int _a;
};

int main()
{
    A* p1 = (A*)malloc(sizeof(A));
    A* p2 = new A;
    free(p1);
    delete p2;

    return 0;
}

我们定义了一个A的类,然后分别用mallocnew的方式开辟了内存,来存放一个A的对象。

对于p1而言,只是开辟了一个A类需要的大小,并把void*指针转化为了A*的指针。
而对于p2,不仅开辟了空间,而且调用了A类的构造函数,把a初始化为0。

所以我们在开辟类的动态内存时,最好使用new,来调用构造函数。

此外,delete也会调用类的析构函数,而free不会。


new[ ] & delete[ ]

new[ ]
如果需要动态分配一个数组,可以使用以下语法:

pointer = new type[size];

其中,size是要分配内存的数组的大小。

例如,以下代码分配了一个包含10个整数的数组的内存,并将地址存储在ptr指针中:

int* ptr = new int[10];

此时,ptr指向一个包含10个未初始化的整数对象的数组。

此外,你还可以对这个数组进行初始化,用大括号即可:

int* ptr = new int[10] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

这样就可以将数组初始化为{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

delete[ ]

同样地,如果之前使用new分配了一个数组,可以使用以下语法释放内存:

delete[] pointer;

方括号内无需填入任何值。

比如:

int* ptr = new int[10];
delete[] ptr;

同样的new[ ] & delete[ ]如果作用与于类,那么会调用相应的构造函数与析构函数。


定位new

那么假如我们用malloc开辟了一个类的空间,还能不能对这个类初始化,调用其构造函数?
是可以的,这就需要定位new了。

在C++中,定位new是一种特殊的new表达式,它允许我们在指定的内存位置上创建对象。通常情况下,new表达式会自动分配内存,并在该内存上创建对象。但是,有时我们希望将对象放置在已经分配的内存中,这就需要使用定位new。

定位new的语法如下:

new (指针) 类型(参数列表);

其中,指针是一个指向已经分配的内存的指针,类型是要创建的对象的类型,参数列表是对象构造函数的参数。

示例:

class MyClass {
public:
  int value;

  MyClass(int v) : value(v) {
    std::cout << "构造函数被调用了" << value << std::endl;
  }
};

int main() {
  // 分配内存
  void* memory = malloc(sizeof( MyClass));

  // 在已分配的内存上创建对象
  MyClass* obj = new (memory) MyClass(10);

  //销毁对象
  obj->~MyClass();

  return 0;
}

在上面的示例中,首先我们使用malloc分配了足够的内存以容纳一个MyClass对象。然后,我们使用定位new在已分配的内存中创建了一个MyClass对象,并传递了一个参数值10给构造函数。我们可以看到构造函数被调用,并输出了相应的消息。

需要注意的是,使用定位new创建的对象必须手动调用析构函数进行销毁,并手动释放相应的内存。

定位new在一些特定的情况下非常有用,例如在实现自定义的内存管理时,或者在某些嵌入式系统中,需要将对象放置在特定的内存地址上。但是在一般的编程中,几乎用不上。


new & delete原理

其实new和delete本质上还是malloc和free,但是C++在两者基础上做了很多优化,最后才得到的new和delete,接下来我将对new和delete进行拆解,带大家看清两者的原理。

对于new来说,其要完成的工作有:

  1. 开辟指定大小的空间
  2. 如果开辟空间失败,抛出异常(对malloc而言是返回空指针)
  3. 如果开辟的空间用于存放对象,那么调用对应的析构函数

我们看看以上三个步骤中,谁是可以通过malloc完成的?
当然是第一步,malloc就可以完成指定的空间的开辟。

抛出异常是C++相比于C语言特有的步骤,可以简单理解为报错。那么C++要如何检测开辟内存失败?
malloc开辟内存失败就会返回空指针,所以我们可以通过malloc的返回值确定是否要抛出异常。所以抛出异常这个步骤也与malloc紧密关联,于是C++将第一步与第二步封装成了一个函数operator new ,它可以同时完成内存开辟和抛出异常,而两者都基于malloc实现。

而我们平常使用的new就是operator new函数+构造函数。
这也就是new的底层原理:将malloc进行封装,完成内存开辟与抛出异常,再额外调用构造函数,完成对象的初始化

搞懂了new的原理,那么delete的原理也就差不多了:
delete也将free进行了一个封装,构成了一个operator delete函数,其完成内存的释放,此外还会调用析构函数,完成对象调用的资源的释放。

不过要注意,delete是先调用析构函数释放对象调用的资源,再调用operator delete完成内存释放。

我们再加上new[]与delete[]不额外讲解了,我们现在对四个操作符进行一次总结:

new的原理

  1. 调用operator new函数申请空间
  2. 在申请的空间上执行构造函数,完成对象的构造

delete的原理

  1. 在空间上执行析构函数,完成对象中资源的清理工作
  2. 调用operator delete函数释放对象的空间

new []的原理

  1. 调用operator new[]函数,在operator new[]中实际调用operator new函数完成N个对象空间的申请
  2. 在申请的空间上执行N次构造函数

delete[]的原理

  1. 在释放的对象空间上执行N次析构函数,完成N个对象中资源的清理
  2. 调用operator delete[]释放空间,实际在operator delete[]中调用operator delete来释放空间

malloc / free 与 new / delete对比

malloc/freenew/delete的共同点是:
都是从堆上申请空间,并且需要用户手动释放。

不同的地方是:

  1. mallocfree是函数,newdelete是操作符
  2. malloc申请的空间不会初始化,new可以初始化
  3. malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可,如果是多个对象,[]中指定对象个数即可
  4. malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型
  5. malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常
  6. 申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分享一个剧本(改编自我)

不知道是不是错过了一个喜欢我的女孩&#xff0c;一个很不错的女孩&#xff0c;当初没勇气表白。去年表白过但女孩表示仅想是永远的朋友&#xff0c;今天翻他的朋友圈发现2021年我生日时&#xff0c;她分享了这首歌曲&#xff0c;还评论Best wishes!!!&#xff0c;高中有一次我…

抖音怎么引导到公众号丨数灵通

抖音是一款非常流行的社交媒体应用程序&#xff0c;用户可以在其中分享短视频和互动内容。许多用户希望通过抖音来引流到他们的微信公众号&#xff0c;以扩大影响力并吸引更多粉丝。以下是一些关于如何在抖音上跳转到微信公众号的科普信息&#xff1a; 1.信息流广告&#xff1a…

PPP协议原理介绍+报文分析+配置指导-RFC1661

个人认为&#xff0c;理解报文就理解了协议。通过报文中的字段可以理解协议在交互过程中相关传递的信息&#xff0c;更加便于理解协议。 因此本文将在PPP协议报文的基础上进行介绍。 关于PPP协议基本原理&#xff0c;可参考RFC1661-The Point-to-Point Protocol (PPP)。 关于P…

Linux的奇妙冒险———vim的用法和本地配置

vim的用法和本地配置 一.vim的组成和功能。1.什么是vim2.vim的多种模式 二.文本编辑&#xff08;普通模式&#xff09;的快捷使用1.快速复制&#xff0c;粘贴&#xff0c;剪切。2.撤销&#xff0c;返回上一步操作3.光标的控制4.文本快捷变换5.批量化操作和注释 三.底行模式四.v…

响应拦截器的 return Promise.reject(res.data.message)

今天在看老师讲解代码的时候,解决了我心中的一些疑惑。 在做excel文件导出的时候,没有告诉浏览器文件的格式是Blod产生了报错。 看下图: 可以看到下面的内容:如果业务成功 返回 res.data 如果业务失败,给出错误信息的提示&#xff0c;将这个错误抛出去。 因此我们在发送一个…

2023春秋杯冬季赛 --- Crypto wp

文章目录 前言Cryptonot_wiener 前言 比赛没打&#xff0c;赛后随便做一下题目 Crypto not_wiener task.py: from Crypto.Util.number import * from gmpy2 import * import random, os from hashlib import sha1 from random import randrange flagb x bytes_to_long(f…

面试题合集

目录 二叉树和动态规划的框架图内容补充数组为什么下标从0开始&#xff1f;windows内存上存储数据采用是什么模式&#xff1f;atoi 和itoa函数的实现字节对齐方式&#xff0c;为什么进行内存对齐&#xff1f;结构体的大小二分查找有重复数字中最左边的数 最右边的数工厂模式 单…

幻兽帕鲁PalWorld服务器搭建详细教程

幻兽帕鲁PalWorld是一款由Pocketpair开发的游戏&#xff0c;融合了多种玩法&#xff0c;其独特的题材和画风吸引了很多玩家。为了更好地进行游戏体验&#xff0c;很多玩家选择自行搭建服务器。本文将详细介绍如何搭建幻兽帕鲁PalWorld服务器。 第一步&#xff1a;购买服务器 根…

面试官灵魂一问,曾写过什么剧本?我:“简单的有,使用Ansible对lnmp架构部署!”

引言&#xff1a;今天带大家使用ansible进行对lnmp的架构部署&#xff0c;并做wordpress网站项目 准备ansible端 db1(安装nginx与php和项目) db2(安装数据库) 并做好管理关联配置 一、创建角色 路径可以自定义&#xff0c;例/root/juben.dir #ansible-galaxy init nginx#an…

.NET 跨平台图形库 SkiaSharp 基础应用

写在前面 SkiaSharp 是适用于 .NET 和 C# 的 2D 图形系统&#xff0c;由开源 Skia 图形引擎提供支持&#xff0c;在 Google 产品中广泛使用。 可以在应用程序中使用 SkiaSharp Xamarin.Forms 绘制 2D 矢量图形、位图和文本。支持跨平台&#xff0c;Windows、Linux、Anroid、IO…

docker-compose搭建redis哨兵模式

文件存放如下图&#xff1a; docker-compose.yml文件内容如下&#xff1a; version: 3.3 services:master:image: redis:3.2.12restart: alwayscontainer_name: redis-mastercommand: redis-server /usr/local/redis/conf/redis.confports:- 6380:6380volumes:- /root/redis/…

java开发——《并发编程》

目录 一.jmm 二.并发了什么 1.只有一个核&#xff08;单核&#xff09;并发还有没有意义 2.单核&#xff0c;还有什么可见性问题 3.并发和并行 三.volitaile 1.变量的可见性问题 2.原因是什么 3.本次修改的变量直接刷到主内存 4.声明其他内存对于这个地址的缓存无效 …

Java Web(三)--CSS

介绍 为什么需要&#xff1a; 在没有 CSS 之前&#xff0c;想要修改 HTML 元素的样式需要为每个 HTML 元素单独定义样式属性&#xff0c;费心费力&#xff1b;CSS 可以让 html 元素(内容) 样式(CSS)分离&#xff0c;提高web 开发的工作效率(针对前端开发)&#xff0c;从而…

3.chrony服务器

目录 1. 简介 1.1. 重要性 1.2. Linux的两个时钟 1.3. 设置日期时间 1.3.1. timedatectl命令设置 1.3.2. date命令设置 1.4. NTP 1.5. Chrony介绍 2. 安装与配置 2.1. 安装&#xff1a; 2.2. Chrony配置文件分析 2.3. 同步时间服务器 2.3.1. 授时中心 2.3.2. 实验…

ssh登录失败:connection closed by foreign host

问题1&#xff1a; ssh登录不上&#xff0c;连接上就断掉 inetd.conf显示2277已打开&#xff0c;ip也没有冲突。 但是这两个文件是空的(size 0k)&#xff1a; dropbear_dss_host_key dropbear_rsa_host_key 把/etc/dropbear里面的东西删掉,重新生成秘钥文件&#xff1a; …

Java强训day1(选择题编程题)

选择题 class Person{//堆public String name;public int age;public double weight;//方法区public void eat(){System.out.println(name"eat()");} }public class TestDemo2 {public static void main(String[] args) {//栈Person p1new Person();Person p2new Per…

基于python豆瓣电影评论的情感分析和聚类分析,聚类分析有手肘法进行检验,情感分析用snownlp

基于Python的豆瓣电影评论的情感分析和聚类分析是一种用于探索电影评论数据的方法。 情感分析 情感分析旨在从文本中提取情感信息&#xff0c;并对其进行分类&#xff0c;如正面、负面或中性。在这里&#xff0c;我们使用了一个名为snownlp的Python库来进行情感分析。Snownlp是…

深入了解达梦数据库的增删查改操作:从入门到精通

目录 前言&#xff1a; 一.达梦数据库的增删改查 1.创建数据库 2.插入数据 3.查看数据 4.删除数据 5.数据 前言&#xff1a; 在当今数字化的时代&#xff0c;数据库已经成为企业和组织的核心资产&#xff0c;是实现高效数据处理、存储和管理的重要工具。达梦数据库&…

linux下msyql自动备份

环境变量配置 vim /etc/profile 追加/usr/local/mysql&#xff0c;MySQL数据库默认安装路径 source /etc/profile 创建定时备份脚本 mkdir /home/mysqlDump/ vim /home/mysqlDump/mysql.sh #!/bin/bash mysqldump -uroot -p123456 bim_ry_prod > /home/mysqlDump/bim…

Qt/QML编程之路:QtMultimedia/Radio(41)

Qt有一个神奇的组件,那就是Qtmultimedia,它有强大的功能: 看看很多多媒体功能,都能在这里找到,不仅audio、video,还有camera、sound和radio。 比如: import QtQuick 2.0 import QtMultimedia 5.0Text {text: "Press Me!"font.pointSize: 24Audio {id: playM…