为什么要使用动态内存分配
C语言中一切操作都是基于内存的,变量和数组都是内存的别名,如何分配这些内存由编译器在编译期间决定,所以定义数组的时候必须指定数组长度,而数组长度是再编译器就必须决定了,但是,程序运行过程中,可能需要使用一些额外空间。所以就需要动态内存分配的支持。
就一句话,C语言初始化都是固定分好了多大内存,突然我想多要一块地,就得使用这些函数。
什么是动态内存的申请和释放?
当程序运行到需要一个动态分配的变量时,必须向系统申请取得堆中的一块所需大小的存储空间,用于存储该变量。当不再使用该变量时,也就是它的生命结束时,要显式释放它所占用的存储空间,这样系统就能对该堆空间进行再次分配,做到重复使用有限的资源。
下面将介绍动态内存申请和释放的函数
1.malloc函数
在C语言中,使用malloc函数来申请内存。函数原型如下:
#include<stdlib.h>
void *malloc(size_t size);
参数size代表需要动态申请的内存的字节数,若内存申请成功,函数返回申请到的内存的起始地址,若申请失败,返回NULL, 在使用该函数时应注意以下几点
1.只关心申请内存的大小,该函数的参数很简单,只有申请内存的大小,单位是字节
2.申请的是一块连续的内存,该函数一定是申请一块连续的区间,可能申请到内存比实际申请的大,但也有可能申请不到,若申请失败,则返回NULL
3.返回值类型是void*,函数的返回值是void*,不是某种具体类型的指针,可以理解成该函数只是申请内存,对在内存中存储什么类型的数据,没有要求,因此,返回值是void*,实际编程中,根据实际情况将void*转换成需要的指针类型
4.显示初始化,注意:堆区是不会自动在分配时做初始化的(包括清零),所以程序中需要显示的初始化
calloc和realloc
(1)void *calloc(size_t num_elements,size_t element_size);
calloc也用于内存分配。malloc和calloc之间的主要区别是后者在返回指向内存的指针之前把它初始化为0。
(2)realloc(void *ptr,size_t new_size);
realloc函数用于修改一个原先已经分配的内存块的大小。如果它用于扩大一个内存,那么这块内存原先的内容依然保留,新增加的内存添加到原先内存块的后面。如果它用于缩小一个内存块,该内存块尾部的部分内存被拿掉,剩余部分内存的原先内容依然保留。
2.free 函数
在堆区上分配的内存,需要用free函数显示释放。函数原型如下:
#include <stdlib.h>
void free(void *ptr);
函数的参数ptr,指的是需要释放的内存的起始地址。该函数没有返回值。使用该函数,也有下面几点需要注意:
(1)必须提供内存的起始地址。调用该函数时,必须提供内存的起始地址,不能提供部分地址,释放内存中的一部分是不允许的。因此,必须保存好malloc返回的指针值,若丢失,则所分配的堆空间无法回收,称内存泄漏。
(2)malloc和free配对使用。编译器不负责动态内存的释放,需要程序员显示释放。因此,malloc与free是配对使用的,避免内存泄漏。
3、memset
void* memset(void* str,int value,size_t num)
按字节设置,进行赋值
功能:以str的起始位置开始的n个字节的内存区域用整数value进行填充
返回值:目标str内存起始地址
说明:
memset用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘ ’或‘/0’
如果用malloc分配的内存,一般只能使用memset来初始化
memset可以方便的清空一个结构类型的变量或数组,它可以一字节一字节地把整个数组设置为一个指定的值
4、memcpy
void* memcpy(void* dest,const void* src,size_t num)
内存拷贝,可以拷贝任意类型
不考虑’\0’
功能:函数memcpy从src的位置开始向后复制num个字节的数据到dest的内存位置
说明:
这个函数在遇到’\0’不会停下来
如果src与dest任何的重叠,复制结果都是未定义的
5、memcmp
int memcmp(const void* ptr1,const void* ptr2,size_t num)
功能:比较内存区域buf1和buf2的前count个字节。该函数是按字节比较的
返回值:
当buf1 < buf2时,返回值<0
当buf1 = buf2时,返回值=0
当buf1 > buf2时,返回值>0
6、memchr
void* memchr(const char* ptr ,int value,sizez_t num)
功能:从buf所指内存区域的前count个字节查找字符ch。
返回值:当第一次遇到字符ch时停止查找。如果成功,返回指向字符ch的指针;否则返回NULL
常见的错误有:对NULL指针进行解引用操作、对分配的内存进行操作时越过边界、释放并非内存分配的内存、试图释放一块动态分配的内存的一部分以及一块动态内存释放之后被继续使用。
因为malloc在科序中经常出现,方便的做法是定义宏语句,在宏语句中调用ma11oc,如果ma1loc失败则退出。以下是这种宏语句的一种实现:
# define MALLOC(p,s)\
if(!(p)=malloc(s))){\
fprintf(stderr,"Insufficient. memory");\
exit(EXIT_FAILURE);\
MALLOC(pi,sizeof(int));
MALLOC(pf,sizeof(float));
C程序中让所有尚未指向实际日标的指针都取NULL值是好习惯。
另一个好习惯是,在转换指针类型时,显式地使用强制类型转换