纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

C标准库堆内存函数 详解C标准库堆内存函数

可可西   2021-06-07 我要评论
想了解详解C标准库堆内存函数的相关内容吗可可西在本文为您仔细讲解C标准库堆内存函数的相关知识和一些Code实例欢迎阅读和指正我们先划重点:c,堆内存下面大家一起来学习吧

概述

C标准库堆内存函数有4个:malloc、free、calloc、realloc其函数声明放在了#include <stdlib.h>中主要用来申请和释放堆内存

堆内存的申请和释放(wikichs)需要发起系统调用会带来昂贵的上下文切换(用户态切换到内核态)十分耗时另外这些过程可能是带锁的难以并行化

对于操作系统而言内存管理的基本单位是页(通常为4K)而不是需要4 Bytes时就给你分配4 Bytes释放4 Bytes时就给你释放4 Bytes

因此为了提升效率操作系统会调用系统api(windows上是VirtualAlloc、VirtualFree其他平台是mmap、munmap)来实现一个Ansi C内存分配器(工作在用户态)供C标准库堆内存函数来使用

不同操作系统Ansi C内存分配器实现方案有所不同

  • windows:MSVCRT.DLL中使用NT heap实现
  • linux:glibc中使用ptmalloc实现
  • Android:使用jemalloc实现

内存碎片与碎片整理

(1)内存碎片(fragmentation):即空闲内存不能被利用分为外部碎片(在分配单元间的未使用的内存)内部碎片(在分配单元中未使用的内存)

(2)内存碎片的罪魁祸首就是小块内存的频繁分配

(3)内存碎片无法避免只能通过内存分配器算法来减少例如:接近大小的内存就近分配释放时能合并就合并从而减少碎片

(4)上面讲的内存碎片指的是虚拟内存碎片OS是不管的OS只管物理内存

(5)平时我们说的内存碎片整理(defragment)或内存紧缩(memory compaction)是指OS对物理内存进行的碎片整理把分开小的物理内存页移动在一起形成一个大的整块

OS整理完物理内存后会用新的物理内存地址来更新虚拟内存与物理内存映射表这些对于上层逻辑都是透明的

虚拟内存是不能进行碎片整理的主要原因是碎片整理会移动内存上层逻辑的指针地址确还是指向老的地址这会导致致命错误

内存分配器的好坏标准

(1)分配和释放的效率

(2)内存分配器的利用率包括以下几个方面:

① 内存对齐导致的不可使用的内存碎片(内部碎片)

② 内存碎片太严重使得分配大块内存时找不到空闲块最后导致内存分配失败(外部碎片)

③ 内存页始终有被使用导致分配器无法及时释放该页的内存占用使得整个内存分配器的内存占用被撑得很大缩不回去

void* malloc( size_t size )

形参size为要求分配的字节数如果函数执行成功malloc返回获得内存空间的首地址如果函数执行失败那么返回值为NULL

由于 malloc函数值的类型为void型指针因此可以将其值类型转换后赋给任意类型指针这样就可以通过操作该类型指针来操作从堆上获得的内存空间

需要注意的是malloc函数分配得到的内存空间是未初始化的可通过调用memset来将其初始化为全0

int* p = (int *) malloc(sizeof(int)*100);
 
if (p == NULL)
{
    printf("Can't get memory!\n");
}
 
memset(p, 0, sizeof(int)*100);

void free( void* ptr )

从堆上获得的内存在程序结束之前系统不会将其自动释放需要程序员来自己管理防止出现内存泄露

free(p);
p = NULL;

void* calloc( size_t num, size_t size )

calloc函数的功能与malloc函数的功能相似都是从堆分配内存

函数返回值为void*如果执行成功从堆上获得size * num大小的堆内存并返回该内存块的首地址如果执行失败函数返回NULL

与malloc函数不同的是calloc函数得到的内存块会被初始化为全0由于提供了2个参数比较适合为数组申请空间可以将size设置为数组元素的空间长度将num设置为数组的容量

int* p = (int *) calloc(100,  sizeof(int));
 
if (p == NULL)
{
    printf("Can't get memory!\n");
}

void *realloc( void *ptr, size_t new_size )

为ptr重新分配大小为size的一块内存空间下面是这个函数的工作流程:

① 如果ptr为NULL则函数相当于malloc(new_size)试着分配一块大小为new_size的内存如果成功将地址返回否则返回NULL

② 如果ptr不为NULL查看ptr是不是在堆中如果不是的话会抛出realloc invalid pointer异常如果ptr在堆中则查看new_size大小

(a)如果new_size大小为0则相当于free(ptr)将ptr指向的内存空间释放掉返回NULL

(b)如果new_size小于原大小只有new_size大小的数据会保存后面地址的数据可能会丢失

(c)如果new_size等于原大小什么都没有做

(d)如果new_size大于原大小则查看ptr指向的位置还有没有足够的连续内存空间如果有的话分配更多的空间返回的地址和ptr相同

如果没有的话在更大的空间内查找如果找到new_size大小的空间将旧的内容拷贝到新的内存中把旧的内存释放掉则返回新地址否则返回NULL

int* p = (int*)malloc(sizeof(int));
*p = 3;
printf("p=%p\n", p);  // p=0000020B2966E310
printf("*p=%d\n", *p); // *p=3

p = (int*)realloc(p, sizeof(int));  // 什么也不做
printf("p=%p\n", p);  // p=0000020B2966E310
printf("*p=%d\n", *p); // *p=3

p = (int*)realloc(p, 1024 * sizeof(int)); // 创建4KB的内存块  注:4KB为一个页面的大小
printf("p=%p\n", p); // p=0000020B29673A50  注:由于不能在原来地址上扩容会将原来地址内存释放并在新地址申请内存块
printf("*p=%d\n", *p); // *p=3

realloc(p, 0);  // 相当于free(p)
p = NULL;

相关文章

猜您喜欢

网友评论

Copyright 2020 www.fresh-weather.com 【世纪下载站】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式