文章目录
1、动态内存分配的意义
在我们平时定义一个变量或一个数组时其大小为固定值。无法做到灵活多变,我们使用动态内存分配可以很好的解决问题。
2、动态内存函数
2.1malloc
void* malloc (size_t size);
其中size为需要申请的字节大小数,类型为size_t;
返回值为void* 成功时,指向函数分配的内存块的指针。此指针的类型始终为void*,可以将其转换为所需的数据指针类型,如果函数未能分配请求的内存块,则返回空指针。
在我们使用动态内存分配函数后,申请得到的内存要释放这里我们使用 :
void free (void* ptr);
来释放获得的内存。
举一个使用的例子。
#include<stdlib.h>
#include<stdio.h>
int main()
{
int *p = (int *)malloc(10 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++)
{
p[i] = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", p[i]);
}
printf("\n");
free(p);
return 0;
}
2.2calloc
void* calloc(size_t num,size_t size);
为num元素数组分配一个内存块,每个元素的大小为字节长,并将其所有位初始化为零。有效的结果是分配(num*size)字节的零初始化内存块。如果大小为零,则返回值取决于特定的库实现(它可能是空指针,也可能不是空指针),但不应取消对返回指针的引用。
#include<stdlib.h>
#include<stdio.h>
int main()
{
int *p = (int *)calloc(10, sizeof(int));
if (p == NULL)
{
return 1;
}
return 0;
}
我们通过查看内存可以看到申请得到的空间确实被初始化为0。
2.3realloc
在我们通过动态内存申请获得内存不够用时,可以使用realloc为其增加空间,其原型为
void* realloc (void* ptr, size_t size);
ptr为指向以前使用malloc、calloc或realloc分配的内存块的指针。或者,这可以是一个空指针,在这种情况下,将分配一个新块(就像调用了malloc一样)。
size为内存块的新大小,以字节为单位。size_t是无符号整数类型。
#include<stdlib.h>
#include<stdio.h>
int main()
{
int *p = (int *)malloc(10 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++)
{
p[i] = i;
}
int *c = (int *)realloc(p, 20 * sizeof(int));//扩大容量
/*if (c != NULL)
{
p = c;
}
*/
for (i = 10; i < 20; i++)
{
c[i] = i;
}
for (i = 0; i < 20; i++)
{
printf("%d ", c[i]);
}
printf("\n");
free(c);
return 0;
}
这里需要说明当内存中有足够的连续空间时则,分配向后的连续空间返回原指针。当连续空间不足时则会重新找一块新内存,将原来的数据复制下来,新的空间也可以使用。
3、 常见的动态内存错误
3.1 对NULL指针的解引用操作
若动态内存申请1失败则会返回空指针,此时对其解引用就会发生错误。
3.2 对动态开辟空间的越界访问
int main()
{
int *p=(int*)malloc(10* sizeof( int));
int i = 0;
for (i = 0; i < 13; i++)//对动态内存分配的空间越界访问。
{
p[i] = i;
}
free(p);
return 0;
}
3.3 对非动态开辟内存使用free释放
int main()
{
int p = 0;
free(p);//对非动态开辟内存使用free释放
return 0;
}
3.4使用free释放一块动态开辟内存的一部分
void text()
{
int *p = (int *)malloc(10 * sizeof(int));//对一部分动态内存申请的释放。
p++;
free(p);
}
3.5 对同一块动态内存多次释放
void text()
{
int *p = (int *)malloc(10 * sizeof(int));//对动态内存的多次释放。
free(p);
free(p);
}
3.6 动态开辟内存忘记释放
虽然程序结束会自动释放,但在大项目中可能会造成严重的内存浪费。
4、 柔性数组
下边是一个柔性数组。
struct student
{
int i;
int a[];//也可写为a[0]
};
4.1柔性数组的特点与使用
结构中的柔性数组成员前面必须至少一个其他成员。
sizeof 返回的这种结构大小不包括柔性数组的内存。
包含柔性数组成员的结构用malloc函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。
struct student
{
int j;
int a[];//柔性数组
}a;
int main()
{
int i = 0;
struct student *p = (struct student*)malloc(sizeof(a)+100 * sizeof(int));//这样相当于a[100]
p->j = 100;
for (i = 0; i<100; i++)
{
p->a[i] = i;
}
free(p);
return 0;
}
4.2 柔性数组的优势
柔性数组对于动态内存申请来说有以下两个优势
1.方便内存释放
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。
2.这样有利于访问速度.
连续的内存有益于提高访问速度,也有益于减少内存碎片。