动态内存管理

简介: 动态内存管理

一、为什么要有动态内存分配

       我们在写代码时,编译器都会自动为我们分配空间,但会有这么两个特点:

        空间开辟⼤⼩是固定的。

       数组在申明的时候,必须指定数组的⻓度,数组空间⼀旦确定了⼤⼩不能调整。

       但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间⼤⼩在程序运⾏的时候才能知道,那数组的编译时开辟空间的⽅式就不能满⾜了。

        C语⾔引⼊了动态内存开辟,让程序员⾃⼰可以申请和释放空间,就⽐较灵活了。

二、动态内存开辟的函数

       2.1 malloc函数

void* malloc (size_t size);

       这个函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针。

        如果开辟成功,则返回⼀个指向开辟好空间的指针。

       如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查。

        返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使⽤的时候使⽤者⾃ ⼰来决定。

       如果参数 size 为0,malloc的⾏为是标准是未定义的,取决于编译器。  

        2.2 calloc函数

void* calloc (size_t num, size_t size);

       函数的功能是为 num 个⼤⼩为 size 的元素开辟⼀块空间,并且把空间的每个字节初始化为0。

       与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。

       2.3 realloc函数

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

        realloc函数的出现让动态内存管理更加灵活。

        有时会我们发现过去申请的空间太⼩了,有时候我们⼜会觉得申请的空间过⼤了,那为了合理的时 候内存,我们⼀定会对内存的⼤⼩做灵活的调整。那 realloc 函数就可以做到对动态开辟内存⼤ ⼩的调整。

       realloc在调整内存空间的是存在两种情况:

       情况1:原有空间之后有⾜够⼤的空间

       情况2:原有空间之后没有⾜够⼤的空间

      情况1

       当是情况1 的时候,要扩展内存就直接原有内存之后直接追加空间,原来空间的数据不发⽣变化。

       情况2

       当是情况2 的时候,原有空间之后没有⾜够多的空间时,扩展的⽅法是:在堆空间上另找⼀个合适⼤⼩ 的连续空间来使⽤。这样函数返回的是⼀个新的内存地址。

三、内存释放函数

       学习完内存开辟函数后,我们随后会来学习内存释放函数。那我们为什么要学习内存释放函数呢?内存开辟函数就是向内存中借一块空间,俗话说:有借有还。所以,我们要来学习内存释放函数来进行归还。所以在学习前记住一句话:只要开辟就要释放。

void free (void* ptr);

       内存释放函数为free函数,如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的。

       如果参数 ptr 是NULL指针,则函数什么事都不做。

四、柔性数组

       柔性数组是什么?相信看到这你有点小懵,难道会柔道的数组称之为柔性数组?非也。柔性数组为结构体中定义的处于结构体最后一个成员的数组(最好不要定义其大小,有的编译器会报错)。

1. typedef struct s
2. {
3.  int a;
4.  int arr[];
5. };

       柔性数组的特点:

       柔性数组的前面必须包含其他结构体成员。

       sizeof求其结构体大小时不会包含柔性数组的大小。

       使用malloc为柔性数组开辟空间时,要大于结构体的大小以符合柔性数组的期待。

        用法如下:

1. 
2. #include<stdio.h>
3. typedef struct s
4. {
5.  int a;
6.  int arr[];
7. }type_a;
8. int main()
9. {
10.   int a = 0;
11.   type_a* p = (type_a*)(malloc(sizeof(type_a) + 100 * sizeof(int)));
12.   p->a = 100;
13.   for (int i = 0; i < 100; i++)
14.   {
15.     p->arr[i] = i;
16.   }
17.   free(p);
18.   p = NULL;
19.   return 0;
20. }
21. 
22.

五、 经典例题

1. #include<stdio.h>
2. #include<stdlib.h>
3. void GetMemory(char* p)
4. {
5.  p = (char*)malloc(100);
6. }
7. void Test(void)
8. {
9.  char* str = NULL;
10.   GetMemory(str);
11.   strcpy(str, "hello world");
12.   printf(str);
13. }

       以上代码运行会有什么后果?

       本题意图为为str开辟出一块空间,对吧?但是,在函数中p为局部变量,运行结束时会销毁,所以,它的地址传不回来,就会开辟失败。但,即使开辟成功,仍有问题,这是为什么呢?咱们在开始时,说过了要有借有还,所以应该要对其进行内存释放,否则,会造成内存泄漏。

1. #include<stdio.h>
2. #include<stdlib.h>
3. void Test(void)
4. {
5.  char* str = (char*)malloc(100);
6.  strcpy(str, "hello");
7.  free(str);
8.  if (str != NULL)
9.  {
10.     strcpy(str, "world");
11.     printf(str);
12.   }
13. }

        以上代码运行会有什么后果?

       在str开辟完空间后,又对其释放,那str能打印出来吗?答案是:不一定。为什么?内存不是没了吗?那里面存放的理应没了呀?对吧。其实,你可以这样理解:你在酒店开了个房间,第二天为了赶火车,不小心把身份证落下了,如果此时房间还没有被打扫,你还能在原位置找到,对吧?打扫了就不能在原位置找到了对吧?理解了我举得例子,相信你就理解了打印结果的情况。

       完!

相关文章
|
1天前
|
存储 缓存 C语言
【c++】动态内存管理
本文介绍了C++中动态内存管理的新方式——`new`和`delete`操作符,详细探讨了它们的使用方法及与C语言中`malloc`/`free`的区别。文章首先回顾了C语言中的动态内存管理,接着通过代码实例展示了`new`和`delete`的基本用法,包括对内置类型和自定义类型的动态内存分配与释放。此外,文章还深入解析了`operator new`和`operator delete`的底层实现,以及定位new表达式的应用,最后总结了`malloc`/`free`与`new`/`delete`的主要差异。
9 3
|
3月前
|
程序员 编译器 C语言
|
5月前
|
编译器 C语言
动态内存管理(1)
动态内存管理(1)
41 4
|
5月前
|
程序员 C语言 C++
动态内存管理(2)
动态内存管理(2)
38 1
|
6月前
|
安全 C++ 开发者
c++动态内存管理(二)
c++动态内存管理(二)
139 0
|
11月前
|
程序员 C语言 C++
动态内存管理-2
动态内存管理
46 0
|
11月前
|
程序员 编译器
动态内存管理-1
动态内存管理
53 0
|
C语言 C++
C++中的动态内存管理
C++中的动态内存管理
|
C语言
动态内存管理(上)
动态内存管理(上)
47 0