动态内存管理及柔性数组

今天我想为大家讲讲动态内存开辟及柔性数组的相关知识。

动态内存管理:

动态内存管理主要涉及:malloc、calloc、realloc和free函数。主要把这四个函数学会并加以灵活的应用,拿着方面的知识点就算基本掌握了,话不多说现在开始。

首先是malloc函数:

void *malloc( size_t size );

malloc的类型的void*,所以在开辟动态内存之前我们要先思考我们要创建的数据类型,并把malloc的类型强行转换成我们需要的类型。它的参数是我们需要的字节个数。现在我为大家举例,例如我们需要创建10个整型的数据:

动态内存管理及柔性数组_第1张图片

但是动态内存的开辟,如果程序员不进行内存的回收可能会出现内存泄漏的问题,那么如何回收开辟的内存呢?这时我们需要用到free函数。

这是free函数的声明:

void free( void *memblock );

使用非常简单,参数就是我们动态开辟的地址。注:开辟的内存地址最后要置成空指针。

free(str);
	str = NULL;

现在再来介绍calloc函数,它和malloc区别就是会对内存开辟中的数值进行初始化。

这是calloc的初始化:

void *calloc( size_t num, size_t size );

我们依旧用创建10个整形的例子:

动态内存管理及柔性数组_第2张图片

动态内存的开辟讲究的是动态,那么该如何实现动态呢?

这是realloc函数派上了用场: 

void *realloc( void *memblock, size_t size );

现在我要将创建的10个整形扩展到20个整形,使用方式如下:

动态内存管理及柔性数组_第3张图片

 其实用realloc函数开辟内存的方式有两种:

动态内存管理及柔性数组_第4张图片

 动态内存管理及柔性数组_第5张图片

 现在我们来谈一谈动态内存开辟常见的几个问题:

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空间的释放,容易造成内存泄漏,不方便用户的使用。最后就是减慢运行速度:每开辟一次空间就要向操作系统申请权限,操作系统同意了才会进行动态内存的开辟。

所以柔性数组的优势显而易见啦!

好了到这就已经把这一章的知识点梳理完毕了,如果不对的地方请指正,康康一定会虚心接受!

你可能感兴趣的:(c语言,开发语言)