动态内存分配
传统数组的缺点:
-
数组长度必须事先定好,且只能是常整数,不能是变量
int len = 5; int a[len];//error,会报错
-
传统形式定义的数组,该数组的内存无法手动释放
在一个函数(主函数或者自定义函数)运行期间,系统为该函数中定义的数组分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会被系统释放
-
数组的长度一旦定义,其长度就不能在改变。即数组的长度不能在函数运行的过程中动态的扩充或缩小
-
传统方式定义的数组不能跨函数使用,在A函数中的数组,在A函数运行期间可以被其它函数使用,但A函数运行完毕之后,A函数中的数组将无法在其他函数使用。
使用动态内存分配可以很好地解决上面的问题
动态分配内存需要用到malloc()
函数,使用例子如下:
int i = 5;//静态分配
int * p = (int *)malloc(4);
*p = 5;
-
malloc
(memory allocate)函数在malloc.h
头文件里定义 -
malloc
函数原型是void * malloc(size_t size)
,可以看到malloc
函数返回的是一个void *
类型的指针(地址) -
malloc
函数只有一个形参,并且形参是整形 -
4
表示请求系统为本程序分配4个字节 -
malloc
函数只能返回第一个字节的地址 -
由于
malloc
只返回第一个字节的地址,因此不知道【变量p
指向的数据】到底占多少个字节,因此需要在malloc(4)
前加上一个强制转换(int *)
,这样就知道【变量p
指向的数据】占的是4个字节。补充:malloc()前是否需要加强制转换
在网上搜了一通后,个人理解是:如果后续代码需要移植到c++去,就要加,如果只是用c,那可加可不加。
参考了这些内容
上面这些内容,大致意思都是:
-
可以不使用强制转换来直接使用malloc,因为在c语言中支持隐式转换
-
上面的内容会有提及说最好不加,都是因为使用强制转换还可能掩盖malloc()声明错误时产生的重要警告
-
如果提及说要加,则一是因为
malloc
默认返回的是void *
类型的,二是因为有些老旧的编译器不支持隐式转换,三是考虑到代码的移植性才需要加(c可以不加,但是后续转c++运行的话不加会出错)
-
-
语句
int * p = (int *)malloc(4);
共分配了8个字节,p
变量占4个字节,【p
所指向的内存】也占4个字节 -
【
p
本身所占的内存】是静态分配的,【p
所指的内存】是动态分配的 -
语句
*p = 5;
中的*p
代表的就是一个int
变量,只不过*p
这个整型变量的内存分配方式和i
变量的分配方式不同
动态内存分配的小例子
利用malloc()
函数确定一维数组的长度:
#include <stdio.h>
int main(void)
{
int a[5];//静态分配
int len;
int * pArr;
puts("请输入数组长度");
scanf("%d",&len);
pArr = (int *)malloc(len * sizeof(int));
return 0;
}
本例中构造的动态一维数组其实类似于这个语句:int pArr[len]
,数组每个元素的类型是int
,数组名称是:pArr
补充
当系统内存空间很小的时候,可能会没有足够空间来使用malloc()
,因此,此时最好加多一个判断语句:
int *p = (int *)malloc(200);
if(p == NULL)
{
printf("malloc error");
exit(-1);
}
free()
malloc()
通常都需要与free()
配套使用:
int * p = (int *)malloc(4);
...;
free(p);
free(p)
表示把【p
所指向的内存】给释放掉
p
本身的内存是静态的,不能手动释放,p
本身的内存只能在p
变量所在的函数运行中止时由系统自动释放
当p指针不再指向上面分配的空间的地址而又没释放掉,那这部分分配的空间就会一直存在,一直到程序执行完毕才会被释放掉
释放掉p所指的内存空间后,为了防止指针p成为野指针(也可以说成防止悬挂指针),通常会再加多一句:
p = NULL;
realloc()
如果用了malloc函数后发现开辟出来的内存空间不够用,可以用realloc
函数重新划分空间。realloc()
函数在<stdlib.h>
中定义,这个函数有两个参数,一个是指向先前开辟的内存块的指针,一个是调整内存空间的字节数。realloc()
函数返回的也是void *
类型的指针。使用realloc()
函数例子如下:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *p;
p = (int *)malloc(20);
...;//一通操作后发现开辟的20字节内存不够用
int *p2;
p2 = (int *)realloc(p,40);//重新调整内存空间
...;
return 0;
}
上面代码中,其实指针p
和指针p2
所指向的地址都是一样的,它们都指向这个空间的起始地址,只是这个内存空间通过realloc()
调整后变大了。因此也可以直接使用这个函数,而不用再定义一个p2
realloc(p,40);
memset()
可用于初始化新开辟空间里面的内容
函数的原型是void * memset(void *str, int c, size_t n)
,使用该函数需要加上#include <string.h>
第一个参数是新开辟的内存空间
第二个参数是要初始化的内容
第三个参数是大小
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int *p = (int *)malloc(4*sizeof(int));
int i;
for(i=0;i<4;i++)
printf("%d ",*(p+i));
printf("\n初始化后:\n");
memset(p,0,12);
for(i=0;i<4;i++)
printf("%d ",*(p+i));
return 0;
}
输出结果:
7493488 0 7471440 0
初始化后:
0 0 0 0
静态内存和动态内存的比较
-
静态内存是由系统自动分配,由系统自动释放
-
静态内存是在“栈”分配的
-
静态内存在函数执行期间可以被其它函数使用,当函数执行完毕之后就不能再被其它函数使用了
void func1() { int i = 0; func2(i); ... } void func2() { ...; }
func1
函数开辟的静态内存中存储着变量i
。当函数func1
执行时,func2
可以访问到变量i
,但是当func1
执行完后,变量i
的内存空间会被释放掉,这时其它函数都访问不了变量i
-
动态内存是由程序员手动分配,手动释放
-
动态内存是在“堆”分配的
-
在某个函数中划分的动态内存,在这个函数执行完毕之后依然不会消失,可以被其它函数使用
void func1(int **q) { *q = (int *)malloc(sizeof(int)); **q = 5; } int main(void) { int *p; func1(&p); printf("%d\n",*p); return 0; }
当
func1
运行完毕后,开辟的内存空间并不会被释放掉,因此依然能访问到这部分的内存空间