前言
动态内存分配是数据结构必不可少的知识。本文章主要介绍了动态内存分配的常规操作,为后续学习更加复杂的数据结构准备。操作函数
头文件存在于stdlib.h中(size_t为无符号类型,定义在stdlib.h中),函数原型如下:
-
void *malloc( size_t size);
-
void free (void *pointer);
malloc会返回连续的字节空间。如果malloc无法获得更多的内存,则会返回空指针。必须确保malloc申请内存成功。malloc返回的起始地址能够满足最严格的边界对齐。
传给freede 指针必须是一个从malloc,calloc,realloc函数返回的指针或者是一个空指针。
-
void *calloc( size_t num_elements,size_t element_size);
-
void *realloc(void *ptr,size_t new_size);
calloc在返回指向内存的指针之前把它初始化为0.
recalloc用来修改一个原先已经分配内存块的大小。可以使内存块扩大或者缩小。新增的内存并没有用任何方法进行初始化,如果原先的内存块大小无法改变,recalloc将会分配另一块大小正确的内存块,并把原来的内存块内容复制到新的块上。如果recalloc的第一个参数是NULL,则它的行为和malloc一摸一样。
封装malloc
更加安全的malloc
#include <stdlib.h>
#define malloc
#define MALLOC(num,type) (type *)alloc((num) * sizeof(type))
extern void *alloc(size_t size);
/***********不易发生错误的内存分配器接口:alloc.h***********/
/*其中“#define malloc”是为了防止用户直接调用库函数malloc,只要包含了这个头文件alloc.h,就不能直接调用库函数malloc,而只能调用自定义函数MALLOC,如果用户要调用库函数malloc编译器会发生错误;*/
#include <stdio.h>
#include "alloc.h"
#undef malloc
void *alloc(size_t size)
{
void *new_nem;
new_nem = malloc(size);
if (new_nem == NULL){
printf("out of memory!\n");
exit(1);
}
return new_nem;
}
/***********不易发生错误的内存分配器实现:alloc.c***********/
/*因为在实现中需要用到库函数malloc,
所以需要用这一句“#undef malloc”取消alloc.h中对malloc的宏定义。*/
内存泄漏&野指针
-
内存分配未成功确使用了它。
-
内存分配成功但是尚未初始化就使用它(引用初值错误(例如数组))。
-
操作越过了分配的边界。
-
对申请了的内存不释放,则会产生内存泄漏,在那些共享内存池的操作系统中,内存泄漏将会榨干可用内存。其他操作系统能够记住每个程序当前拥有的内存段,当程序停止运行时,则所有分配给它但未被释放的内存都归还给内存池。但是着也很危险,一个持续分配内存却一点不释放的内存程序最终将耗尽可用内存。同时可能会干扰其他程序,使得操作系统崩溃。
-
不要访问已经被free了的内存。如果你对一个指向动态分配的内存的指针进行了复制,而这个指针的几份拷贝散步程序各处,这很危险,必须确保程序中所有使用这块内存的得放在这个内存释放之前停止对它的使用。
-
释放一块内存的一部分是非法的,但是realloc函数可以缩小一块动态分配的内存,有效得释放它尾部的部分内存。
总结
动态内存分配结合某些特定的数据结构用处更大,还有待探索。