今天我想为大家讲讲动态内存开辟及柔性数组的相关知识。
动态内存管理:
动态内存管理主要涉及:malloc、calloc、realloc和free函数。主要把这四个函数学会并加以灵活的应用,拿着方面的知识点就算基本掌握了,话不多说现在开始。
首先是malloc函数:
void *malloc( size_t size );
malloc的类型的void*,所以在开辟动态内存之前我们要先思考我们要创建的数据类型,并把malloc的类型强行转换成我们需要的类型。它的参数是我们需要的字节个数。现在我为大家举例,例如我们需要创建10个整型的数据:
但是动态内存的开辟,如果程序员不进行内存的回收可能会出现内存泄漏的问题,那么如何回收开辟的内存呢?这时我们需要用到free函数。
这是free函数的声明:
void free( void *memblock );
使用非常简单,参数就是我们动态开辟的地址。注:开辟的内存地址最后要置成空指针。
free(str);
str = NULL;
现在再来介绍calloc函数,它和malloc区别就是会对内存开辟中的数值进行初始化。
这是calloc的初始化:
void *calloc( size_t num, size_t size );
我们依旧用创建10个整形的例子:
动态内存的开辟讲究的是动态,那么该如何实现动态呢?
这是realloc函数派上了用场:
void *realloc( void *memblock, size_t size );
现在我要将创建的10个整形扩展到20个整形,使用方式如下:
其实用realloc函数开辟内存的方式有两种:
现在我们来谈一谈动态内存开辟常见的几个问题:
1.非动态内存的空间用free释放。
2.用free释放部分的动态内存。
3.对动态开辟的空间进行越界访问。
4.对NULL指针进行解引用操作(开辟的地址没有进行是否为null指针的判断)
5.对同一块动态内存进行多次释放。
6.开辟的动态内存没有进行内存的释放。
现在我们来介绍几个经典的面试题:
void GetMemory(char *p) {
p = (char *)malloc(100);
}
void Test(void) {
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
请问运行Test 函数会有什么样的结果?
1.str传进GetMemory想要接收p的地址,已知形参是实参的一份临时拷贝,对形参的修改不会影响实参,故str依旧是空指针,strcpy将hello world拷贝到空指针去程序报错。
2.动态内存开辟没有释放空间,造成内存泄漏。
char *GetMemory(void) {
char p[] = "hello world";
return p; }
void Test(void) {
char *str = NULL;
str = GetMemory();
printf(str);
}
请问运行Test 函数会有什么样的结果?
1.str接收到了动态内存开辟的p的地址,但是出了函数以后,由于p占据的是栈空间的地址,p的空间将会被操作系统回收,所以失去了访问p空间的权限。造成了野指针的使用。
2.不会打印出hello world。
void GetMemory(char **p, int num) {
*p = (char *)malloc(num);
}
void Test(void) {
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
请问运行Test 函数会有什么样的结果?
1.会打印出hello
2.造成内存泄漏。
void Test(void) {
char *str = (char *) malloc(100);
strcpy(str, "hello");
free(str);
if(str != NULL)
{
strcpy(str, "world");
printf(str);
}
}
请问运行Test 函数会有什么样的结果?
1.空间已经被释放再将world拷贝到指针所指向的空间中造成野指针使用的问题。
修改方法:将free放在最后。
柔性数组:
typedef struct st_type
{
int i;
int a[];//柔性数组成员
}type_a;
柔性数组的概念非常简单,就是结构体中最后一个结构体变量是个大小不确定的数组,我们用malloc为结构体开辟空间(开辟的大小大于结构体的大小以适用于数组)。结构体的大小与数组大小无关(数组大小不计)。数组的前面至少有一个结构体其他成员!
int i = 0;
type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));
//业务处理
p->i = 100;
for(i=0; i<100; i++) {
p->a[i] = i; }
free(p);
有人思考可以将结构体中的数组换成一个指针,再为这个指针开辟一个动态内存。那么问题来了,这样做会让内存碎片化,其次要进行两次free空间的释放,容易造成内存泄漏,不方便用户的使用。最后就是减慢运行速度:每开辟一次空间就要向操作系统申请权限,操作系统同意了才会进行动态内存的开辟。
所以柔性数组的优势显而易见啦!
好了到这就已经把这一章的知识点梳理完毕了,如果不对的地方请指正,康康一定会虚心接受!