zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C语言动态内存分配函数malloc(),calloc(),realloc()用法对比分析

C语言 函数 分析 用法 对比 分配 malloc 动态内存
2023-06-13 09:13:43 时间

引入

在C中我们开辟内存空间有两种方式 :

1.静态开辟内存 :

int a;
int b[10];

特点: 所开辟的内存是在中开辟的固定大小的 ,如a是4字节 ,数组b是40字节 ,并且数组在申明时必须指定其长度 , 如果是全局数组的话,内存是在编译时分配好的,如果是局部变量数组的话,运行时在上静态分配内存。

不管是全局数组还是局部数组,它们都有一个特点,那就是数组大小是确定的,可以更改其中的值,但是不能改变数组的大小。

那如果我们想在程序运行时才确定一个数组的大小 , 前两种在栈上分配内存的方法显然是不行的。

int n;
scanf("%d", &n);
int a[n];

这样编写会在编译时出错 , 编译器会提醒[ ]中应为常量表达式 , 在C中定义数组时可以用的有以下几种 ,例:

#define N 10
enum NUM{
	M=10
};
int a1[N];
int a2[10];
int a3[M];

PS:

C中const int n =10 ; n并不能作为数组长度定义数组 , 但C++中则可以 。

2.动态开辟内存 :

在C中动态开辟空间需要用到三个函数 : malloc(), calloc(), realloc() ,这三个函数都是向中申请的内存空间. 在堆中申请的内存空间不会像在栈中存储的局部变量一样 ,函数调用完会自动释放内存 , 需要我们手动释放 ,就需要free()函数来完成.

1.malloc()

void * malloc(size_t size)
  1. malloc()函数会向中申请一片连续的可用内存空间
  2. 若申请成功则返回指向这片内存空间的指针 ,若失败 ,则会返回NULL, 所以我们在用malloc()函数开辟动态内存之后, 一定要判断函数返回值是否为NULL.
  3. 返回值的类型为void*型, malloc()函数并不知道连续开辟的size个字节是存储什么类型数据的 ,所以需要我们自行决定 ,方法是在malloc()前加强制转 ,转化成我们所需类型 ,如: (int*)malloc(sizeof(int)*n).
  4. 如果size为0, 此行为是未定义的, 会发生未知错误, 取决于编译器
int *p = NULL;
int n = 0;
scanf("%d", &n);
p = (int*)malloc(sizeof(int) * n);
if(p != NULL){
    //....需要进行的操作
}

2.free()

void free(void* ptr)//ptr是开辟的动态内存的地址

在堆中申请的内存空间不会像在栈中存储的局部变量一样 ,函数调用完会自动释放内存 , 如果我们不手动释放, 直到程序运行结束才会释放,。

即堆中这片内存中的数据已经不再使用, 但它一直占着这片空间,且这块空间不能被利用,如果不及时清理会造成内存泄漏,导致内存逐渐被占满。

  1. 如果ptr没有指向使用动态内存分配函数分配的内存空间,则会导致未定义的行为。
  2. 如果ptr是空指针,则该函数不执行任何操作。
  3. 此函数不会更改ptr本身的值,因此它仍指向相同(现在已经无效)的位置(内存)
  4. 在free()函数之后需要将ptr再置空 ,即ptr = NULL;如果不将ptr置空的话 ,后面程序如果再通过ptr会访问到已经释放过无效的或者已经被回收再利用的内存, 为保证程序的健壮性, 一般我们都要写ptr = NULL; .

PS :

free()不能重复释放一块内存(浅拷贝问题的来源)

free(ptr);
free(ptr);//报错

free()具体用法, 举个例子 :

int *p = NULL;
int n = 0;
scanf("%d", &n);
p = (int*)malloc(sizeof(int) * n);
if(p != NULL){
    //....需要进行的操作
}
//操作完成 ,不再使用这片内存空间
free(p);
p = NULL;

3.calloc()

void * calloc(size_t num,size_t size)//传入两个参数

第一个参数是存储单元的数量,第二个参数是存储单元的大小。

  1. calloc()函数功能是动态分配num个大小(字节长度)为size的内存空间 .
  2. 若申请成功 ,,返回指向这片内存空间的指针 ,若失败 ,则会返回NULL, 所以我们在用calloc()函数开辟动态内存之后, 一定要判断函数返回值是否为NULL.
  3. 返回值的类型为void型, calloc()函数虽然分配num个size大小的内存空间 ,但还是不知道存储的什么类型数据 ,所以需要我们自行决定 ,方法是在calloc()前加强制转 ,转化成我们所需类型 ,如: (int)calloc(num, sizeof(int)).
  4. 如果size与num有一个或都为0, 此行为是未定义的, 会发生未知错误, 取决于编译器

与malloc()函数的区别:(关键)

calloc()函数会在返回地址之前将所申请的内存空间中的每个字节都初始化为0 。所以如何我们对申请的内存空间的内容要求初始化,那么可以很方便的使用calloc函数来完成这个需求。

long *num;
num = (long*)calloc(100,sizeof(long));//创建了100个4字节的存储单元,共计400字节。

//对比与malloc,malloc的参数是将calloc中的两个参数进行相乘.传入总的字节数,本质上都是开辟到堆区,区别只是是否要初始化。
int *p;
p = (int*)malloc(sizeof(int) * n);

4.realloc()

void * realloc(void * ptr,size_t size)

realloc()函数让动态内存管理更加灵活 .在程序运行过程中动态分配内存大小。

  1. ptr为需要调整的内存地址。
  2. size为调整后需要的大小(字节数)。
  3. 若调整成功, 返回值为调整大小后内存的起始位置(也就是指向调整后内存的指针), 若失败(当没有内存可以分配时, 一般不会出现), 则返回NULL, 所以还是要对返回值判空。
  4. 如果ptr是空指针, 则和原来的开辟的内存一样,没有任何变化。

PS : realloc()函数在扩大内存空间时有两种情况

1.ptr所指的内存后有足够的内存空间用来扩展 ,如图 1 2.ptr所指内存后没有足够的空间来扩展 ,如图 2

当第二种情况时, 若申请新的内存空间成功, 会将ptr所指向的内存中的内容拷贝到新的内存空间中, ptr所指向的内存会被释放, 返回新得内存地址, 若不成功 ,ptr 所指内存不会被释放, 函数返回NULL

5.小结

1).malloc()和calloc()函数用法一样, 唯一的区别是calloc()会对所申请内存的每个字节初始化为0 2).malloc(), calloc(), realloc()申请的内存不再使用时 ,一定要用free()释放 ,否则会造成内存泄漏 3).p = realloc(ptr, size)函数返回值不为空时, 释放内存时不需写free(ptr) ,只需写free(p)

详见C Primer Plus P396