C语言中内存管理主要分为以下几块:
- 栈区
- 堆区
- 全局区或静态区
- 字符常量区
- 程序代码区
一般栈区都是由系统自动分配回收,但是栈区大小是有限制的,windows下一般为程序分配的栈内存为2M左右。超出就会抛出stack overflow错误.
如下:
void main(){
//40M
//静态内存分配
int a[1024 * 1024 * 10];
getchar();
}
上面的代码运行时就会抛出 stack overflow。这种情况下,就需要程序人员,手动在堆区分配与释放内存。
在C语言中内存管理主要通过以下几个函数进行:
- malloc()
- realloc()
- calloc()
- free()
malloc与calloc函数都是进行堆内存分配,区别主要在于传参形式不一样:
//8M
int * p = malloc(1024 * 1024 * 2 * sizeof(int));
//8M
int * p2 = calloc(1024 * 1024 * 2,sizeof(int));
realloc函数用于重新分配内存。分为两种情况:
- 缩小:缩小的那一部分数据会丢失。
- 增大:
- 如果当前内存段后面有需要的内存空间,直接扩展这段内存空间,realloc返回原指针
- 如果当前内存段后面的空闲字节不够,那么就使用堆中的第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,并将原来的数据库释放掉,返回新的内存地址
- 如果申请失败,返回NULL,原来的指针仍然有效
示例:
void main(){
int len;
printf("第一次输入数组的长度:");
scanf("%d", &len);
//int* p = malloc(len * sizeof(int));
int* p = calloc(len, sizeof(int));
int i = 0;
for (; i < len; i++){
p[i] = rand() % 100;
printf("%d,%#x\n", p[i], &p[i]);
}
int addLen;
printf("输入数组增加的长度:");
scanf("%d", &addLen);
//内存不够用,扩大刚刚分配的内存空间
//1.原来内存的指针 2.内存扩大之后的总大小
int* p2 = realloc(p, sizeof(int)* (len + addLen));
if (p2 == NULL){
printf("重新分配失败");
free(p);
p = NULL;
return;
}
//重新赋值
i = 0;
printf("--------------------------\n");
for (; i < len + addLen; i++){
p2[i] = rand() % 200;
printf("%d,%#x\n", p2[i], &p2[i]);
}
//手动释放内存
if (p2 != NULL){
free(p2);
p2 = NULL;
}
getchar();
}
free函数用于释放内存。如上所示。
Note:同一块内存区不能多次释放,释放完之后(指针仍然有值),一般地给指针置NULL,标志释放完成,避免这种情况。重新分配内存成功后,如果是原地址,上面即是p =p2.如果是新开辟的内存,则p会被系统自动释放,所以后面只释放p2即可。
下面再看一个内存分配的例子:
void main(){
int len = 4;
char ** args = malloc(sizeof(char*)*len);
int i = 0;
for ( ; i < 4; i++)
{
args[i] = malloc(sizeof(char)* 20);
sprintf(args[i],"array[%d]",i);
}
for ( i = 0; i < 4; i++)
{
free(args[i]);
}
free(args);
getchar();
}
上面的示例中,我们进行了两次开辟,好像跟上面讲的不一样,数组开辟空间后,为啥单个元素还要再次开辟,而上面的数组却没有?其实不然。
这里我们定义的是一个字符串数组。但是C中是没有字符串的,每一个字符串其实是一个字符数组,所以,它相当于是一个二维数组,所以需要再次分配内存。
我们再举个二维数组,动态分配的例子:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int n1,n2;
int **array,i,j;
printf("请输入所要创建的动态数组的第一维长度:");
scanf("%d",&n1);
printf("请输入所要创建的动态数组的第二维长度:");
scanf("%d",&n2);
array=(int**)malloc(n1*sizeof(int*)); //第一维
for(i=0;i<n1; i++)
{
array[i]=(int*)malloc(n2* sizeof(int));//第二维
}
for(i=0;i<n1;i++)
{
for(j=0;j<n2;j++)
{
array[i][j]=i*n2+j+1;
printf("%d\t",array[i][j]);
}
printf("\n");
}
for(i=0;i<n1;i++)
{
free(array[i]);//释放第二维指针
}
free(array);//释放第一维指针
return 0;
}
这两个例子,对比着看,相信就不会有啥疑惑了。