个人博客网址:https://ljsblog.com
动态内存分配(十)
在不知道所需要的空间大小的情况下,这时就可以使用动态内存开辟。
当开辟的空间不再使用时,用free函数来释放calloc、malloc或realloc所分配的内存空间。
动态内存函数
free函数
free函数是用来释放动态开辟的空间
格式:
void free(void *ptr)
free函数的头文件: stdlib.h
malloc函数
malloc函数会在内存开辟一块连续可用的空间,并返回一个指向它的指针,若开辟失败,则返回NULL,所以malloc的返回值一定要检查。
格式:
void *malloc(size_t size)
头文件: stdlib.h
例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
int i=0;
//向内存申请10个整形空间
//因为malloc的返回值的类型是void*,所以需要用(int*)将其转换化为int*类型
int* p=(int*)malloc(10*sizeof(int));
//检查开辟成功还是失败
if(p==NULL)//判断指针是否为空,为空则代表开辟空间失败
{
//打印错误原因
printf("%s\n",strerror(errno));
}
else
{
//赋值并打印
for(i=0;i<10;i++)
{
*(p+i)=i;
}
for(i=0;i<10;i++)
{
printf("%d ",*(p+i));//0 1 2 3 4 5 6 7 8 9
}
}
//释放空间
free(p);
//此时,虽然空间被释放掉,但p依然指向那个地址,所以需要将p赋一个空指针
p=NULL;
return 0;
}
calloc函数
calloc函数会在内存开辟一块连续可用的空间,并返回一个指向它的指针。
与malloc的不同是,calloc会把开辟的空间的每个字节初始化为零。
格式:
void *calloc(size_t nitems, size_t size)
nitems:元素个数。
size:元素大小。
头文件: stdlib.h
例:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
int main()
{
int i=0;
int* p=(int*)calloc(10,sizeof(int));
//向内存申请10个大小为int的空间并将每个字节初始化为0
if(p==NULL)
{
printf("%s\n",strerror(errno));
}
else
{
for(i=0;i<10;i++)
{
printf("%d ",*(p+i));//0 0 0 0 0 0 0 0 0 0
}
}
free(p);
p=NULL;
return 0;
}
realloc函数
realloc函数可以调整动态开辟空间的大小。
使用realloc函数的注意事项:
- 原有空间后有足够大的空间,就直接在原有内存后追加空间,原来空间内容不发生变化。
- 原有空间后没有足够大的空间,则另找一个合适大小的连续空间来使用,并将原内存的数据移动到新的空间,释放旧的内存,函数返回的将是一个新空间的地址。
- 需要用一个新的指针变量来接受realloc函数的返回值。
格式:
void *realloc(void *ptr, size_t size)
ptr:之前开辟的内存块地址
size:调整后新大小
例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
int i=0;
int* p=(int*)malloc(20);
//使用malloc开辟了20个字节空间
int* p1=NULL;
if(p==NULL)
{
printf("%s\n",strerror(errno));
}
else
{
for(i=0;i<5;i++)
{
*(p+i)=i;
}
}
//希望有40个字节空间
p1=(int*)realloc(p,40);
//使用realloc调整内存空间为40个字节
if(p1!=NULL)
{
p=p1;
for(i=5;i<10;i++)
{
*(p+i)=i;
}
for(i=0;i<10;i++)
{
printf("%d ",*(p+i));//0 1 2 3 4 5 6 7 8 9
}
}
//释放内存
free(p);
p=NULL;
return 0;
}
常见的动态内存错误
1.对NULL指针解引用操作
错误示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* p=(int*)malloc(40);
//有可能malloc开辟空间失败返回的NULL
*p=10;
return 0;
}
2.对动态开辟空间的越界访问
错误示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i=0;
int* p=(int*)malloc(10*sizeof(int));//10个int
if(p==NULL)
{
return 0;
}
for(i=0;i<=10;i++)//越界
{
*(p+i)=i;
}
free(p);
p=NULL;
return 0;
}
3.对非动态开辟的内存使用free释放
错误示例:
#include <stdio.h>
#include <stdilb.h>
int main()
{
int a=10;
int* p=&a;
free(p);//对非动态开辟的内存使用free释放
p=NULL;
return 0;
}
4.使用free函数释放动态开辟内存的一部分
错误示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i=0;
int* p=(int*)malloc(10*sizeof(int));
if(p==NULL)
{
return 0;
}
for(i=0;i<10;i++)
{
*p++=i;
}
//p已经不在原来的位置了
free(p);
p=NULL;
return 0;
}
5.对同一块动态内存多次释放
错误示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i=0;
int* p=(int*)malloc(10*sizeof(int));
if(p==NULL)
{
return 0;
}
free(p);
free(p);
return 0;
}
6.忘记释放动态开辟内存
错误示例:
#include <stdio.h>
#include <stdlib.h>
int main()
{
while(1)
{
malloc(1);
}
return 0;
}
柔性数组(C99)
在结构体内,结构体中的最后一个元素可以是未知大小的数组,而且有特定的形式[]或者[0],叫做柔型数组成员
注:
- 结构体中柔性数组成员前面必须至少有一个成员
- 在计算结构体大小时,不包含柔性数组成员
#include <stdio.h>
#include <stdlib.h>
struct S
{
int n;
int arr[];
};
int main()
{
int i=0;
struct S* p=(struct S*)malloc(sizeof(struct S)+5*sizeof(int));
struct S* p1=NULL;
p->n=1;
if(p==NULL)
{
return 0;
}
for(i=0;i<5;i++)
{
p->arr[i]=i;
}
p1=(struct S*)realloc(p,44);
if(p1!=NULL)
{
p=p1;
for(i=5;i<10;i++)
{
p->arr[i]=i;
}
for(i=0;i<10;i++)
{
printf("%d\n",p->arr[i]);
}
}
free(p);
p=NULL;
return 0;
}
C语言内存分区
1.栈区(stack)
栈区是一种先进后出的内存结构,由编译器自动分配释放,存放函数的返回值、局部变量、函数参数、返回地址等。
2.堆区(heap)
用于动态内存分配。一般由程序员分配释放,若程序员不释放,程序结束时由操作系统回收。
3.数据段(静态区)(static)
存放全局变量,静态数据,程序结束时由操作系统释放。
4.代码段
存放函数体的二进制代码。