动态内存开辟
(一)三种方式
内存中存在三种空间,分别是栈区(用来存放局部变量和函数形式的参数),堆区(动态内存分配),静态区(存放全局变量和静态变量)
(二)动态内存函数
1.malloc和free
malloc是申请开辟动态内存分配空间函数,头文件为<stdlib.h> 和<malloc.h>,在MSDN上解释为void *malloc( size_t size ); 如果开辟成功,那么返回值为指向这块空间的指针,如果开辟失败,那么返回NULL。
free是释放内存空间函数,和malloc一起使用。
#include <stdio.h>
#include <malloc.h>
int main()
{
int* p = (int*)malloc(40);
//开辟内存空间
if (p == NULL)//开辟失败
{
return -1;
}
for (int i = 0;i < 10;i++)
{
*(p + i) = i;
}
free(p);
//释放内存空间
return 0;
}
在内存中显示如下,
2.calloc
calloc也是开辟内存空间函数,MSDN上解释为void *calloc( size_t num, size_t size );,与malloc函数不同,calloc函数在堆区开辟空间后初始化为0,其中,num为元素的个数,size为每个元素的字节长度。
#include <stdio.h>
#include <string.h>
int main()
{
int* p = (int *)calloc(10,sizeof(int));
//开辟内存空间
if (p == NULL)//开辟失败
{
printf("%d", strerror(errno));
return -1;
}
for (int i = 0;i < 10;i++)
{
printf("%d ", *(p + i));
//输出为0 0 0 0 0 0 0 0 0 0
}
free(p);
//释放内存空间
return 0;
}
3.realloc
realloc也是内存开辟函数,要在原有开辟的基础上再次开辟,相当于增加空间,在MSDN上解释为void *realloc( void *memblock, size_t size );,头文件为<stdlib.h>或者<malloc.h>,其中memblock是原来开辟内存空间的起始地址,size是再次开辟的内存空间之后的总大小,单位是字节。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int* p = (int *)calloc(10,sizeof(int));
//开辟内存空间
if (p == NULL)//开辟失败
{
printf("%d", strerror(errno));
return -1;
}
int* ch = (int*)realloc(p, 20 * sizeof(int));
if (ch != NULL)
{
p = ch;//开辟成功
}
else
{
return -1;//开辟失败
}
for (int i = 10;i < 20;i++)
{
*(p + i) = i;
}
for (int j = 0;j < 20;j++)
{
printf("%d ", *(p + j) );
}
free(p);
//释放内存空间
return 0;
}
realloc函数在内存中开辟空间的形式如下,如果后续空间足够大,那么就直接开辟;如果后续空间不足,那么将向后寻找直至找到足够开辟空间的位置,如果找不到,那么开辟失败,返回NULL。如图所示,
(三)常见错误
1.对空指针进行解引用
#include <stdio.h>
int main()
{
int* p = (int*)malloc(20);
*p = 20;//error
return 0;
}
如果开辟内存空间失败,那么p为空指针,无法进行操作。应该加判断,
if (p == NULL)
{
return -1;
}
else
{
*p = 20;
}
2.对动态内存开辟的越界访问
#include <stdio.h>
int main()
{
int* p = (int*)malloc(5*sizeof(int));
if (p == NULL)
{
return -1;
}
for (int i = 0;i < 10;i++)//error
{
*(p + i) = i;//赋值
}
for (int j = 0;j < 10;j++)
{
printf("%d ", *(p + j));//打印
}
free(p);
return 0;
}
在这里malloc函数开辟的空间为五个整型的大小,在赋值时,却对十个整型进行赋值,导致动态内存开辟的越界访问,运行显示错误。
3.与free有关的错误
free释放非动态开辟内存
#include <stdio.h>
int main()
{
int a = 10;
int* p = &a;
free(p);//error
p = NULL;
return 0;
}
这里的p开辟的地址被放在栈区,而free是对堆区的内存进行操作,运行报错。
free释放动态内存空间的一部分
#include <stdio.h>
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
if (p == NULL)
{
return -1;
}
for (int i = 0;i < 10;i++)
{
*p++ = i;
}
free(p);
return 0;
}
free指向的应该是释放空间的头地址,p++过后,p已经不再指向起始地址了,运行产生错误。
free对同一块空间多次释放
#include <stdio.h>
int main()
{
int* p = (int*)malloc(10 * sizeof(int));
free(p);
free(p);
return 0;
}
两次释放p的空间,error,可以在每次释放之后给p赋值为NULL以避免这种错误。
动态开辟内存忘记释放/内存泄漏
动态开辟内存或通过free手动释放或者在程序结束时进行自我回收。如果不进行free释放空间,程序一直运行且不结束的话,则会存在内存泄漏,除非程序重启。
(四)柔性数组
结构体中允许最后一个成员是未知大小的数组,这就叫做柔性数组。如下例所示,有两种表示方式,(注意数组之前一定至少有一个其他成员)
struct st_type1
{
int x;
int a[];
};
//或者
struct st_type1
{
int x;
int a[0];
};
当用sizeof计算含有柔性数组成员的时候,其结果不考虑柔性数组成员。包含柔性数组成员的结构用malloc函数进行动态内存分配,且分配的内存应该大于数组的大小以适应柔性数组成员
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
struct st_type1
{
int x;
int a[0];
};
int main()
{
struct st_type1*p = (struct st_type1*)malloc(sizeof(struct st_type1) + 10 * sizeof(int));
//给柔性数组成员开辟足够的空间
if (p == NULL)
{
printf("%S\n", strerror(errno));
return -1;
}
p->x = 10;
for (int i = 0;i < 10;i++)//赋值
{
p->a[i] = i;
}
for (int j = 0;j < 10;j++)//打印
{
printf("%d ", p->a[j]);
}
struct st_type1* pr = (struct st_type1*)realloc(p, sizeof(struct st_type1) + 20 * sizeof(int));
//如果空间不够,再次开辟
if (pr == NULL)//开辟失败
{
printf("%S\n", strerror(errno));
return -1;
}
else
{
p = pr;
}
free(p);
return 0;
}
这样柔性数组和结构体结合,动态内存开辟以改变结构体的大小,如果不使用柔性数组的话,代码如下,
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
struct st_type1
{
int x;
int *a;
};
int main()
{
struct st_type1*p = (struct st_type1*)malloc(sizeof(struct st_type1));
if (p == NULL)
{
printf("%S\n", strerror(errno));
return -1;
}
p->x = 10;
p->a = (int *)malloc(10*sizeof(int));//开辟a指向的空间
for (int i = 0;i < 10;i++)//赋值
{
p->a[i] = i;
}
for (int j = 0;j < 10;j++)//打印
{
printf("%d ", p->a[j]);
}
int* pr = (int *)realloc(p->a, 10 * sizeof(int));
if (pr == NULL)
{
printf("%S\n", strerror(errno));
return -1;
}
else
{
p->a = pr;
}
free(p->a);//释放a指向的空间
free(p);//释放结构体的空间
return 0;
}