【C语言进阶篇】动态内存分配和数组的动态内存分配

您所在的位置:网站首页 叙述c语言程序的内存分配方式 【C语言进阶篇】动态内存分配和数组的动态内存分配

【C语言进阶篇】动态内存分配和数组的动态内存分配

2023-09-18 05:58| 来源: 网络整理| 查看: 265

本文主要介绍了C语言中常用的内存函数和数组的动态内存分配,并稍微提了一嘴柔性数组。动态内存分配是C语言中十分重要的一环,其中对二维数组的动态内存分配是个难点,需要多思考。 在这里插入图片描述

目录 1. 为什么存在动态内存分配2. 动态内存函数mallocfreecallocrealloc 3. 动态内存分配数组3.1 一维数组的动态分配和使用3.2 二维数组的动态内存分配和使用 4. 柔性数组

1. 为什么存在动态内存分配

要开辟一个数组,我们可以很简单做到:

int arr[10]; char str[20]; struct S s[10];

我们可以很容易地声明整型数组、字符数组、结构体数组…

但是,这样声明出来的数组有很明显的缺点:

数组大小固定当数组固定大小很大时,可能会栈溢出(Stack overflow) 在这里插入图片描述

但实际上,很多情况下我们并不知道自己要开辟多大的空间,这些很多时候是只有在程序跑起来才知道的,所以这就引出了动态内存分配。

动态内存分配出来的空间是在堆区开辟的,这是它与通过定义数组分配出来的空间最本质的区别。

2. 动态内存函数

动态内存分配是需要调用动态内存函数实现的,下面介绍四种内存函数,点击超链接即可转到官方解释。

malloc

在这里插入图片描述 malloc 是我们见得最多的动态内存函数。

它会向内存申请一块连续可用的空间,空间大小是 size 个字节,并返回指向这块空间的指针。

它返回的是个指针,所以在使用它时要用指针接收:

char* ch = malloc(sizeof(char) * size1); int* arr = malloc(sizeof(int) * size2); struct S* s = malloc(sizeof(stuctt S) * size3);

但这样并不严谨,因为 malloc 返回类型是空指针,所以在接受它的返回值时最好再对它进行强制类型转换:

char* ch = (char*)malloc(sizeof(char) * size1); int* arr = (int*)malloc(sizeof(int) * size2); struct S* s = (struct S*)malloc(sizeof(stuctt S) * size3);

但是,当动态内存开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查:

char* ch = (char*)malloc(sizeof(char) * size); if(ch == NULL) { perror("fun_name"); return; } free

在这里插入图片描述 有动态内存分配的地方就一定会有 free 函数。

它的参数是一个指针,指针指向的空间是动态内存分配出来的一块空间。它会释放掉这块空间,将这块空间还给操作系统。

它释放的空间一定是要动态内存分配出来的,这块空间一定是在堆区的,否则会引发异常: 在这里插入图片描述 用 free 去释放栈区的空间,这是万万不能够的!!

此外,如果传过去的指针是个 NULL 空指针,free 就会纯纯摆烂,啥也不干。

释放空间之后,原指针就没有任何意义,但 free 不会自动给它置成空指针,此时他就成为一个野指针,所以我们要即及时将其置成空指针:

int* ptr = (int*)malloc(sizeof(int) * size); if(ptr == NULL) { perror("fun_name"); return; } ... free(ptr); ptr = NULL; calloc

在这里插入图片描述 它的返回类型和 malloc 一样,参数部分则有不同:

num :元素个数 size :每个元素的大小(byte)

所以它的作用就是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为 0。

所以它的使用和 malloc 基本一致:

int *p = (int*)calloc(10, sizeof(int)); if(NULL == p) { perror("fun_name"); return; } ... free(p); p = NULL;

相比 malloc ,它只是把分配的空间全部初始化为 0: 在这里插入图片描述

realloc

在这里插入图片描述 虽然我们能通过 malloc 或 calloc 函数动态内存分配一块空间,但这块空间分配完成后大小也是固定的,如果空间满了需要扩容或空间多了需要缩减,这时 realloc 函数就登场了。

realloc 的作用是对已经动态分配的一块空间再次分配。

它有两个参数:

memblock:要调整的内存地址,这块内存是动态内存分配得到的size:以字节为单位的新大小

它会返回调整之后的内存起始位置。

关于调整之后的内存起始位置会出现以下两种情况:

与原来内存的起始位置相同与原来内存的起始位置不同 当要缩小原有的内存时,原来的内存空间已经足够,此时它的返回值就是原来内存的起始位置。当要扩大原有的内存时,又有两种情况: 原有空间之后有足够大的内存时,直接在原内存的基础上再开辟后边几个连续的空间,此时它的返回值是原来内存的起始位置;原有空间之后没有足够大的内存进行扩容时,此时会在堆空间上另找一个合适大小的连续空间来使用,这样函数返回的是一个新的内存地址,与原来内存的起始位置不同。这时会拷贝原来空间的内容到新的空间的相应位置,原空间就被释放掉了。

所以,在使用 realloc 函数时就要注意用一个临时指针接收,当内存调整成功后再将临时指针赋给原指针:

int main() { int* ptr = (int*)malloc(100); if (ptr == NULL) { preeor("main"); return; } ... //定义一个临时指针接收新地址 int* tmp = NULL; tmp = (int*)realloc(ptr, 1000); if (tmp != NULL) { ptr = tmp; } ... free(ptr); ptr = NULL; return 0; } 3. 动态内存分配数组

对于动态内存分配我们主要用于动态开辟一维和二维数组。 下面就看看开辟数组的方法和正确使用这块空间并释放。

3.1 一维数组的动态分配和使用

动态开辟一维数组还是比较简单的:

int main() { int size = 10; //给整形数组动态分配 int* arr = (int*)malloc(sizeof(int) * size); if (arr == NULL) { perror("main"); return; } //给数组赋值 for (int i = 0; i perror("main"); return; } //访问数组并赋值 int count = 1; for (int i = 0; i perror("main"); return; } //对每个一维数组开辟空间 for (int i = 0; i perror("main"); return; } } //访问数组元素并赋值 int count = 1; for (int i = 0; i //用数组指针形式申请一个ROW行COL列的二维数组 int(*arr)[COL] = (int(*)[COL])malloc(sizeof(int) * ROW * COL); if (arr == NULL) { perror("main"); return; } //访问数组成员并对其赋值 int count = 1; for (int i = 0; i perror("main"); return; } ... free(p); p = NULL;


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3