目录
1.为什么要动态内存管理
2.malloc 和 free(引用stdlib.h)
1.malloc
2.free
3.calloc 和 realloc(引用stdlib.h)
1.calloc
2.realloc
4.常见的动态内存管理错误
5.柔性数组
1.什么是柔性数组:
2.柔型数组的特点:
1.柔型数组成员前必须至少有一个成员。
2.sizeof求结构体的大小时,不包含柔型数组的大小
3.在动态分配内存空间时,分配的内存空间必须大于结构体的大小,以适应柔型数组大小。
3.柔性数组的使用
7.C/C++程序在内存中的存储区域划分
int value = 20; //vlaue值为20,在内存中占了4个字节。
int value[10] = {0}; //数组value一共有10个元素,每个元素值为0,
// 数组value在内存中占40个字节。
通过上面例子我们可以总结出一下几点:
1.在声明变量是,内存空间已经开辟好了,并且开辟的是固定值
2.在声明数组的时候,必须指定数组的长度(即数组的元素个数),并且之后不能改变数组的长度。
那么,这就可能会造成内存的浪费。举个例子:你是36码的脚,但是店老板只卖你40码的鞋,那这双鞋能穿吗?理论上当然是能穿的。但是,这么做肯定会不合脚,有可能40码脚的人都没鞋穿。
所以,在C语言中引入了动态内存分配的概念,程序员可以自己按照需求申请和释放空间,这给我们带来了很大的灵活性。
C语言中提供了动态内存分配的函数:
void* malloc(size_t size)
在C语言中,我们通过malloc函数,可以在堆区申请一块空间供我们使用。通过上面代码,我们分析一下,malloc怎么使用,malloc函数向内存申请一块 连续可用 的空间,size 表示我们要开辟多大的一块内存空间(单位是字节)。
#include
#include
#include
int main()
{
int* p = (int*)malloc(40);
if(p == NULL)
{
printf("%s\n",strerror(errno));
return 1;
}
}
注意:
1.当malloc成功申请到一块内存空间时,则返回该空间的起始地址。
2.当malloc没有申请到一块内存地址时,则返回空指针,所以我们必须在申请后进行检查。
3.返回类型是void*,所以malloc不知道开辟空间的类型,这个需要使用者根据需要自己定义
4.如果 size 为0,这个是C语言标准没有定义的,是根据编译器情况而定的。
free函数是专门用来释放我们申请的内存空间的。
void free(void* ptr)
注意:
1.ptr是我们申请的内存空间的起始地址。
2.如果ptr不是指向我们所开辟得内存空间,这是free函数未定义的,编译不会通过。
3.如果ptr是个空指针,那么free函数什么也不会做。
举个例子:
#include
int main()
{
int i = 0;
int* p =(int*)malloc(40);
if(p == NULL) //检查p是否为空指针
{
return 1; //如果是空指针,直接结束程序
}
for(i = 0 ; i < 10 ; i++)
{
*(p+i)=i+1; //赋值
}
for(i = 0 ; i < 10 ; i++)
{
printf("%d ",*(p+i)); //打印 1 2 3 4 5 6 7 8 9 10
}
return 0;
}
calloc和malloc作用都是开辟一块内存空间。
void* calloc(size_t num,size_t size);
从名字上看,calloc 和 malloc其实相差不多,代码上也只是比malloc多了个num,那么它和malloc有什么区别呢。
1.calloc会在内存中开辟num个大小为size的元素空间,并把空间的每个字节初始化为0
2.calloc 和 malloc的区别就是,calloc会在返回地址前,把空间全部初始化为0
这样看,其实calloc就是malloc的孪生兄弟罢了,只不过比malloc多了一项初始化的功能。想不想要初始化,这取决于程序员本身。但是,要注意的是,calloc的初始化,是把空间的每个字节初始化为0,且只能为0。
realloc作用就是扩容。
void* realloc(void* ptr,size_t size)
1.ptr是要调整的内存空间的地址
2.size是调整后的空间大小
3.realloc调整可能面对下面两种情况
a.内存空间后面还有足够大的空间,那么内存地址不变,内存空间变大。
b.内存空间后面没有足够大的空间,那么会找一个新的足够大的内存地址,将原内 内存空间的内容复制拷贝到新的内存地址空间,内存地址改变。
1.对NULL指针(空指针)解引用操作
int* p =(int*)malloc(4);
*p = 20; //如果p是NULL指针,就会有问题
free(p);
2.对非动态内存分配的空间进行free释放
int a = 20;
int* p = &a;
free(p); //如果释放非动态分配内存空间,就会有问题
3.free释放了动态分配内存空间的一部分
#include
int main()
{
int i = 0;
int* p =(int*)malloc(40);
if(p == NULL) //检查p是否为空指针
{
return 1; //如果是空指针,直接结束程序
}
for(i = 0 ; i < 10 ; i++)
{
*p = i+1;
p++;
}
free(p); //此时p的存储的已经不是动态内存分配空间的起始地址,且会造成动态内存分配的越界访问
return 0;
}
4.对动态内存空间的越界访问
#include
int main()
{
int i = 0;
int* p =(int*)malloc(40);
if(p == NULL) //检查p是否为空指针
{
return 1; //如果是空指针,直接结束程序
}
for(i = 0 ; i < 10 ; i++)
{
*p = i+1;
p++; // i = 10 时,就会是造成越界访问
}
return 0;
}
5.对同一块动态分配内存空间多次释放
int* p =(int*)malloc(4);
*p = 20;
free(p); //frees释放后,p为野指针
free(p); //若对野指针进行释放,会出问题
6.动态分配的内存空间没有free释放(内存泄漏)
#include
void test()
{
int* a = (int*)malloc(4);
...
if(flag == 1)
{
return; //若满足条件,跳出函数
}
...
free(a); //如果满足条件,那么空间将永远不会得到释放,造成内存泄漏
}
int main()
{
test();
return 0;
}
如果我们申请了空间,却不归还,就会造成内存泄漏,倘若我们总是申请却不归还,我们就有可能造成系统的崩溃,导致死机。所以,现在我们就要养成良好的代码书写习惯,有借有还。
在结构体中,最后一个元素是元素数未知大小的数组,就被称为柔型数组成员
struct test
{
int a; //柔性数组前必须至少有一个成员变量
int arr[]; //arr就是柔性数组
};
sizeof(struct test); //输出的是 4
struct test* p = (struct test*)malloc(sizeof(struct test)+10*sizeof(int))
// 这样我们就得到了10个int类型元素大小的柔性数组
#include
struct test
{
int a;
int arr[];
};
int main()
{
int i = 0;
struct test* p = (struct test*)malloc(sizeof(struct test)+10*sizeof(int));
p.a = 20;
for(i = 0 ; i < 10 ; i++)
{
p->arr[i] = i+1;
}
free(p);
return 0;
}