一、为什么需要动态内存分配?
在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];
但我们对于开辟空间的需求 , 往往不限于此 , 最常见的定义数组时数组大小在程序运行时才知道的 , 静态开辟就已经无能为力 . 这就是我们需要动态开辟内存空间的原因。
二、动态开辟内存的方法
在C中动态开辟空间可以使用三个函数 :
1.malloc()
2.calloc()
- realloc()
这三个函数都是向堆中申请的内存空间。
在堆中申请的内存空间不会像在栈中存储的局部变量一样 ,函数调用完会自动释放内存 , 需要我们手动释放 ,就需要free()函数来完成.
下面让我们来看看malloc()函数的用法:
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){
//....需要进行的操作
}
这时就相当于创建了一个数组 p[n] ,这个n的值并不需要像定义一个普通数组一样必须是常量, 可以使程序运行时得出的, 或是用户输入的
2.free()
- 格式
void free(void* ptr);
在堆中申请的内存空间不会像在栈中存储的局部变量一样 ,函数调用完会自动释放内存 , 如果我们不手动释放, 直到程序运行结束才会释放, 这样就可能会造成内存泄漏, 即堆中这片内存中的数据已经不再使用, 但它一直占着这片空间, (通俗说就是就是占着茅坑不拉屎), 所以当我们申请的动态内存不再使用时 ,一定要及时释放 。
1).如果ptr没有指向使用动态内存分配函数分配的内存空间,则会导致未定义的行为。2).如果ptr是空指针,则该函数不执行任何操作。3).此函数不会更改ptr本身的值,因此它仍指向相同(现在已经无效)的位置(内存)4).在free()函数之后需要将ptr再置空 ,即ptr = NULL;如果不将ptr置空的话 ,后面程序如果再通过ptr会访问到已经释放过无效的或者已经被回收再利用的内存, 为保证程序的健壮性, 一般我们都要写ptr = NULL;
注意 :
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;