1.动态内存函数
1.malloc函数和free函数
malloc函数的函数声明:
void* malloc (size_t size);
malloc函数和free函数的头文件都是
#include <stdlib.h>
malloc函数向内存申请一块连续可用的空间,并返回指向这块空间的指针。 如果开辟成功,则返回一个指向开辟好空间的指针。 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己 来决定。 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器
free函数是专门做动态内存的回收和释放的。
free的函数声明为:
void free (void* ptr);
对于free函数: 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。 如果参数 ptr 是NULL指针,则函数什么事都不做。
(free函数在后面的动态内存函数中也会用到,其效果和使用方法是一样的)
举一个关于malloc函数和free函数的例子
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>//malloc和free的头文件
#include <string.h>
#include <errno.h>
int main()
{
int arr[10] = { 0 };
int* p = (int*)malloc(40);//返回值为void* 要自己设置类型
//开辟成功则返回一个指向开辟好空间的指针
//开辟失败则返回NULL指针
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//使用
int i;
for (i = 0; i < 10; i++)
{
*(p + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d\n", *(p + i));
}
//释放掉内存
free(p);//动态内存的释放和回收
//若free释放的不是动态内存 情况未定义
p = NULL;
return 0;
}
2.calloc函数
calloc函数也是用来动态内存分配,它的用法与malloc相似,但是多了一个参数,并且它可以将开辟出来的空间初始化,其作用相当于malloc函数加上memset函数的结合体。
calloc的函数声明为:
void* calloc (size_t num, size_t size);
calloc函数的功能是:
为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
与函数 malloc 的区别在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。 ?
举个例子:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
int* p = (int*)calloc(10, sizeof(int));//calloc会将开辟出来的内存初始化成0
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//打印
int i;
for (i = 0; i < 10; i++)
{
printf("%d\n", *(p + i));
}
//释放
free(p);
p = NULL;
return 0;
}
如果对开辟出来的空间又初始化为0的需求 那用calloc函数在合适不过了。
3.realloc函数
它的函数声明为:
void* realloc (void* ptr, size_t size);
realloc函数的出现让动态内存管理更加灵活。 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的时 候内存,我们一定会对内存的大小做灵活的调整。那 realloc 函数就可以做到对动态开辟内存大小 的调整。 ptr 是要调整的内存地址 size 调整之后新大小 返回值为调整之后的内存起始位置。 这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间 realloc在调整内存空间的是存在两种情况: 情况1:原有空间之后有足够大的空间 情况2:原有空间之后没有足够大的空间
当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发生变化。 当是情况2 的时候,原有空间之后没有足够多的空间时,扩展的方法是:在堆空间上另找一个合适大小 的连续空间来使用。这样函数返回的是一个新的内存地址。 由于上述的两种情况,realloc函数的使用就要注意一些。 ?
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int main()
{
int* p = (int*)malloc(40);
if (p == NULL)
{
printf("%s\n", strerror(errno));
}
//使用
int i;
for (i = 0; i < 10; i++)
{
*(p + i) = i + 1;
}
//扩容
int* ptr = (int*)realloc(p, 80);//扩容到80个字节 相当于原来有40 又加上40
//不要直接用原来的指针接收 如果realloc返回NULL 可能导致数据流失
if (ptr != NULL)
{
p = ptr;
}
for (i = 0; i < 10; i++)
{
printf("%d ", *(p + i));
}
//realloc(NULL, 40) 等价于 malloc(40);
return 0;
}
2.常见的动态内存错误
1.对NULL指针的解引用操作。
2.对动态开辟空间的越界访问。
3.对非动态开辟内存使用free释放
4.使用free释放一块动态开辟内存的一部分
5.对同一块动态内存多次释放
6.动态开辟内存忘记释放(内存泄漏)
3.柔性数组
1.柔性数组的定义方法
struct S
{
int i;
int a[0];//柔性数组成员
};
有些编译器可能会报错,那么可以改成下面这种:
struct S
{
int i;
int a[];//柔性数组成员
};
2.柔性数组的特点和使用
结构中的柔性数组成员前面必须至少一个其他成员。 sizeof 返回的这种结构大小不包括柔性数组的内存。 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大 小,以适应柔性数组的预期大小。 ?
使用:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
struct S
{
int n;
int arr[];
};
int main()
{
int sz = sizeof(struct S);//4 结构体内存不计算柔性数组
printf("%d\n", sz);
//柔性数字的使用
struct S* ps = (struct S*)malloc(sizeof(struct S) + 40);
if (ps == NULL)
{
return 1;
}
int i;
for (i = 0; i < 10; i++)
{
*(ps->arr + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", ps->arr[i]);
}
struct S* str = (struct S*)realloc(ps, sizeof(struct S) + 80);
if (str != NULL)
{
ps = str;
}
//释放
free(ps);
ps = NULL;
return 0;
}
3.柔性数组的优势
上面的使用也可以写成下面这种形式:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
struct S
{
int n;
int* arr;
};
int main()
{
//柔性数字的使用
struct S* ps = (struct S*)malloc(sizeof(struct S));
if (ps == NULL)
{
return 1;
}
ps->n = 100;
int* p = (int*)malloc(40);
if (p != NULL)
{
ps->arr = p;
}
int i;
for (i = 0; i < 10; i++)
{
*(ps->arr + i) = i;
}
for (i = 0; i < 10; i++)
{
printf("%d ", ps->arr[i]);
}
struct S* str = (struct S*)realloc(ps, sizeof(struct S) + 80);
if (str != NULL)
{
ps = str;
}
//释放
free(ps->arr);
ps->arr = NULL;
free(ps);
ps = NULL;
return 0;
}
与下面的相比 用柔性数组的好处显而易见
首先,方便内存释放。 如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。
第二,这样有利于访问速度。
连续的内存有益于提高访问速度,也有益于减少内存碎片。 ?
|