C++基础(三) —— 内存分配

news/2024/10/30 11:32:01/

文章目录

  • 概念
    • 物理地址内存的分配与释放
    • 虚拟用户进程空间内存的分配与释放
  • allocator模板类
  • new delete
  • malloc free
  • strcpy 与 memcpy 与 memset
    • strcpy
    • memcpy
    • memset


概念

物理地址内存的分配与释放

主要采用链表结构

使用了一个名叫page的结构体管理物理内存,结构体中包括了页的大小、页的状态以及指向相邻页的指针。

Linux内核使用这些指针来构建了一个逻辑链表,当需要分配内存的时候,会从链表中查找第一个空闲页并把它标记为已使用。

释放内存的时候,会把相应的页标记为空闲,并把它插入到链表对应的位置

虚拟用户进程空间内存的分配与释放

C++语言层次
new delete
智能指针 栈上的对象出作用域自动析构 自动管理内存的分配与释放

C语言层次
malloc free
malloc() 分配的是虚拟内存。
如果分配后的虚拟内存没有被访问的话,虚拟内存是不会映射到物理内存的,这样就不会占用物理内存了。

系统调用
sbrk() brk() mmap()
管理进程的堆(heap)空间。

问:什么场景下 malloc() 会通过 brk() 分配内存?又是什么场景下通过 mmap() 分配内存?
malloc() 源码里默认定义了一个阈值:
如果用户分配的内存小于 128 KB,则通过 brk() 申请内存;
如果用户分配的内存大于 128 KB,则通过 mmap() 申请内存;

allocator模板类

#include <iostream>
#include <memory>int main() {std::allocator<int> allocator;// 在堆上动态的分配大小为5*sizeof(int)的内存int* ptr = allocator.allocate(5);int* ptrnum = new int[5];int abc[5];  // abc也是指针// 构造对象for (int i = 0; i < 5; ++i) {allocator.construct(ptr + i, i);allocator.construct(ptrnum + i, i);allocator.construct(abc + i, i);}// 访问对象for (int i = 0; i < 5; ++i) {std::cout << ptr[i] << " ";std::cout << ptrnum[i] << " ";std::cout << abc[i] << " ";}std::cout << std::endl;// 销毁对象for (int i = 0; i < 5; ++i) {allocator.destroy(ptr + i);allocator.destroy(ptrnum + i);  }// 释放内存allocator.deallocate(ptr, 5);delete ptrnum;ptrnum = nullptr;return 0;
}

new delete

堆上分配内存

T* ptr = new T; // 分配单个对象的内存并构造对象
T* arr = new T[N]; // 分配对象数组的内存并构造对象
delete ptr; // 释放单个对象的内存并调用析构函数
delete[] arr; // 释放对象数组的内存并调用每个对象的析构函数

new 运算符在堆上分配的内存可以通过相应的 delete 运算符来释放,从而销毁对象并释放内存。

动态:
为了简化内存管理,C++11 引入了智能指针(如 std::shared_ptr 和 std::unique_ptr),它们提供了更安全和更方便的内存管理机制。智能指针可以自动管理动态分配的内存,避免显式使用 delete,从而减少了内存泄漏和资源管理的错误。

malloc free

堆上分配内存
void* malloc(size_t size);
malloc() 返回一个指向分配内存块的指针,该内存块大小为 size 字节。分配的内存块在堆上连续存储,可以手动管理其使用和释放。

void free(void* ptr);
free输入的是指向内存块的指针

问1:malloc(1) 会分配多大的虚拟内存
malloc() 在分配内存的时候,并不是老老实实按用户预期申请的字节数来分配内存空间大小,而是会预分配更大的空间作为内存池。
具体会预分配多大的空间,跟 malloc 使用的内存管理器有关系,我们就以 malloc 默认的内存管理器(Ptmalloc2)来分析。

#include <stdio.h>
#include <malloc.h>int main() {printf("使用cat /proc/%d/maps查看内存分配\n",getpid());//申请1字节的内存void *addr = malloc(1);printf("此1字节的内存起始地址:%x\n", addr);printf("使用cat /proc/%d/maps查看内存分配\n",getpid());//将程序阻塞,当输入任意字符时才往下执行getchar();//释放内存free(addr);printf("释放了1字节的内存,但heap堆并不会释放\n");getchar();return 0;
}

程序输出:
此1字节的内存起始地址d73010

之后,使用cat /proc/…/maps查看内存分布情况。我在 maps 文件通过此 1 字节的内存起始地址过滤出了内存地址的范围。

[root@xiaolin ~]# cat /proc/3191/maps | grep d730
00d73000-00d94000 rw-p 00000000 00:00 0                                  [heap]

可以看到,堆空间的内存地址范围是 00d73000-00d94000,这个范围大小是 132KB,也就说明了 malloc(1) 实际上预分配 132K 字节的内存。
但是程序里打印的内存起始地址是 d73010,而 maps 文件显示堆内存空间的起始地址是 d73000,为什么会多出来 0x10 (16字节)呢?这个问题在问2中。

问2:free() 函数只传入一个内存地址,为什么能知道要释放多大的内存?
malloc 返回给用户态的内存起始地址比进程的堆空间起始地址多了 16 字节,这个多出来的 16 字节就是保存了该内存块的描述信息,比如有该内存块的大小。
在这里插入图片描述

这样当执行 free() 函数时,free 会对传入进来的内存地址向左偏移 16 字节,然后从这个 16 字节的分析出当前的内存块的大小,自然就知道要释放多大的内存了。

strcpy 与 memcpy 与 memset

内存数据拷贝函数
strcpy 和 memcpy 是 C 语言中的库函数,用于内存数据的拷贝操作。它们有不同的使用方式:
memset 是 C 语言中的库函数,用于将一块内存区域设置为指定的值

strcpy是提供了对字符串的复制,memcpy是内存的复制,对复制的内容没有限制,使用范围更广!!!
strcpy和memcpy主要有以下3方面的区别。
复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy。

strcpy

char* strcpy(char* dest, const char* src);
strcpy 用于将一个以 null 结尾的字符串从源地址 src 复制到目标地址 dest,并返回目标地址的指针。
实例

char source[] = "Hello, World!";
char destination[20];
strcpy(destination, source);

memcpy

void* memcpy(void* dest, const void* src, size_t n);
memcpy 用于将源地址 src 的前 n 字节的数据复制到目标地址 dest,无返回值。
示例:

int source[] = {1, 2, 3, 4, 5};
int destination[5];
memcpy(destination, source, sizeof(source));

需要注意的是,使用这两个函数时,需要确保目标地址 dest 具有足够的空间来容纳要复制的数据。

一个数据报文的构成实例:

   char buffer[kBufferSize];memset(buffer, 0, sizeof(buffer));// Headerbuffer[0] = 0x5A;buffer[1] = 0xA5;// CMD_IDconst char* CMD_ID = "00000000000000001";memcpy(buffer+2, CMD_ID, strlen(CMD_ID));// Frame_Typebuffer[19] = 0xD1;// Packet_Typebuffer[20] = 0x01;// Frame_Nobuffer[21] = 0x01;// Sub_Packet_Typebuffer[22] = 0x00;buffer[23] = 0x00;// Time_Stampsrand(time(NULL));int time_stamp = rand() % 1000000;memcpy(buffer+24, &time_stamp, sizeof(int));// Xfloat x = 123.456f;memcpy(buffer+28, &x, sizeof(float));// Yfloat y = 789.012f;memcpy(buffer+32, &y, sizeof(float));// Zfloat z = 345.678f;memcpy(buffer+36, &z, sizeof(float));// Versionint version = 1;memcpy(buffer+40, &version, sizeof(int));// CRC16uint16_t crc = 0;for (int i = 0; i < 42; i++) {crc += (uint8_t)buffer[i];}memcpy(buffer+42, &crc, sizeof(uint16_t));// Endbuffer[44] = 0x96;

memset

memset 是 C 语言中的库函数,用于将一块内存区域设置为指定的值
void* memset(void* ptr, int value, size_t num);
memset 将指针 ptr 指向的内存区域的前 num 字节都设置为值 value。它返回指向 ptr 的指针。
它可以用来快速地将一块内存区域设置为特定的值,例如将数组全部设置为零或将某个标记数组全部设置为特定的标记值。

示例:

int array[5];
memset(array, 0, sizeof(array));  // 将数组全部设置为零char str[10];
memset(str, 'A', sizeof(str));  // 将 str 数组的每个元素都设置为字符 'A'
for (int i = 0; i < sizeof(str); i++) printf("%c ", str[i]);

需要注意的是,memset 的参数 value 是一个整数,会被解释为无符号字符。因此,如果需要将内存区域设置为非零的特定值,需要确保该值在无符号字符的范围内。

在 C++ 中,也可以使用 std::fill 算法或使用初始化语法来实现相似的功能,以提供更安全和易用的方式来初始化和设置内存。


http://www.ppmy.cn/news/281073.html

相关文章

乌云 drops_免费赠品发布:“ Drops of Gold”壁纸

像元素一样&#xff0c;金的颜色在自然界中是罕见的发现-这使得它变得更加可取。 只需在秋季的高峰期对植物进行简单的拍照即可&#xff0c;只需对其进行一些微调&#xff0c;并对其进行精细的颜色更改即可使其特别捕捉到可以描述为黄金时刻的事物。 这款“ Drops of Gold”壁…

线条边框简笔画图片大全_植物简笔画素材大全赶紧收藏起来,一定用的上!

点击上方“实用文章”关注我们&#xff0c;定期为您推送精彩实用的文章。 植物简笔画素材大全赶紧收藏起来&#xff0c;一定用的上&#xff01; 花简笔画 向日葵简笔画 梅花简笔画 怎么画牵牛花简笔画 怎么画荷花简笔画 菊花简笔画怎么画 仙人掌简笔画 康乃馨简笔画 植物简笔画…

含泪整理最优质动物 植物cc0高清摄影图片素材,你想要的这里都有

因为工作的原因&#xff0c;我会经常使用到素材网站&#xff0c;各个平台的模式不太一样&#xff0c;内容也各有特点。刚开始接触素材网站都是一头雾水&#xff0c;走了很多弯路&#xff0c;一些很简单的操作都花去很长的操作时间.....今天给大家安利动物 植物cc0高清摄影图片素…

比人工更智能更有趣的植物识别--形色

无论你是否爱花草&#xff0c;你一定有过这样的经历&#xff0c;偶然间看见一株小植物&#xff0c;很想要知道它叫什么然而身边却没有一个可以询问的人。 “这是什么花&#xff1f;”你们面面相觑&#xff0c;有人说&#xff1a;“我查一下&#xff1f;”但却无从下手… 相信…

最佳下载酷壁纸的网站

Getting bored with your desktop wallpaper or the wallpaper on your mobile device? Because we use our PCs and mobile devices so much, it’s nice to have a choice of cool backgrounds to look at. 厌倦了您的桌面墙纸或移动设备上的墙纸? 由于我们经常使用PC和移动…

[Python] 开发植物大战僵尸游戏

用python开发植物大战僵尸 早期的植物大战僵尸是由vc开发的&#xff0c;作者的想法是非常地有特色&#xff0c;所以今天我们来探讨下植物大战僵尸游戏的进入场景怎么开发。 我们首需要用到如下几个文件 程序文件功能game.py在游戏中就运行的文件initgame.py游戏主菜单选择关…

17.6:迪瑞克斯啦算法

迪瑞克斯啦算法 这个算法研究的是&#xff1a;有向的&#xff0c;没有负权重&#xff0c;可以有环的图。 这个算法主要研究的是&#xff1a;给出的节点到这张图的其他节点的最短路径是多少。用一个表表示出来。 思路&#xff1a; 如下图所示&#xff0c;我们想要求出a节点到其…

dpdk rte_ring

rte_ring_enqueue卡住是什么原因 以下回答来自于GPT-3.5&#xff1a; 如果程序卡在rte_ring_enqueue函数&#xff0c;可能有几种原因。首先&#xff0c;rte_ring_enqueue函数可能会阻塞&#xff0c;直到有空间可用于将数据包添加到环形缓冲区中。如果环形缓冲区已满&#xff0…