【C语言】想要节省空间,你必须要知道——动态内存管理(附通讯录动态内存版源码)(一)

简介: 【C语言】想要节省空间,你必须要知道——动态内存管理(附通讯录动态内存版源码)(一)

【C语言】想要节省空间,你必须要知道——动态内存管理 (附通讯录动态内存版源码)


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

2.    动态内存函数的介绍

  2.1    malloc

  2.2    free

         malloc和free通常配合一起使用:

  2.3    calloc

  2.4    realloc

3.    常见的动态内存错误

4.    几个经典的笔试题

   题目1:

   代码分析:

   代码改正:

   题目2:

   代码分析:

   代码改正:

   题目3 :

   代码分析:

   代码改正:

   题目4 :

   代码分析:

   代码改正:

5.    柔性数组

通讯录(动态储存版本)源码


(附通讯录动态内存版源码))


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

我们已经掌握的内存开辟方式有:


int val = 20; //在栈空间上开辟四个字节

char arr[10] = {0}; //在栈空间上开辟10个字节的连续空间


但是上述的开辟空间的方式有两个特点:


1 . 空间开辟大小是固定的。

2 . 数组申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。


但是对于空间的需求,不仅仅是上述的情况。

有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。

这时候就只能试试动态存开辟了。


2.    动态内存函数的介绍


2.1    malloc


C语言为我们提供了一个动态内存开辟的函数


描述


C 库函数 void *malloc(size_t size) 分配所需的内存空间,并返回一个指向它的指针


声明


void *malloc(size_t size)


参数


size – 内存块的大小,以字节为单位。


返回值


该函数返回一个指针 ,指向已分配大小的内存。如果请求失败,则返回 NULL


注意点:

1.如果开辟成功,则返回一个指向开辟好空间的指针

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

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

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

5.malloc开辟的内存空间是在堆上的,不会自动释放空间

2.2    free

由于malloc是在堆空间上开辟内存,不会被自动释放,容易造成内存泄漏

这时候,C语言里提供了一个free函数,来人为释放动态内存开辟的空间,将空间还给操作系统


描述


C 库函数 void free(void *ptr) 释放之前调用 calloc、malloc 或 realloc 所分配的内存空间


声明


void free(void *ptr)


参数


ptr – 指针指向一个要释放内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果传递的参数是一个空指针,则不会执行任何动作


返回值


该函数不返回任何值


注意:


1.如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的

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

3.通常在free完之后,要 ptr=NULL;将指针给置空,否则当释放了空间,这块空间的指针仍然存在,就会造成一个野指针


malloc和free通常配合一起使用:


举个栗子


#include

int main()

{

//代码1

int num = 0;

scanf("%d", &num);

int arr[num] = {0};

//代码2

int* ptr = NULL;

ptr = (int*)malloc(num*sizeof(int));

if(NULL != ptr)//判断ptr指针是否为空

{

int i = 0;

for(i=0; i

{

*(ptr+i) = 0;

}

}

free(ptr);//释放ptr所指向的动态内存

ptr = NULL;//是否有必要?答案是很有必要

return 0;

}


2.3    calloc

描述

C 库函数 void *calloc(size_t nitems, size_t size) 分配所需的内存空间,并返回一个指向它的指针。malloc 和 calloc 之间的不同点是malloc 不会设置内存为零,而 calloc设置分配的内存为零


声明


void *calloc(size_t nitems, size_t size)

参数


nitems 要被分配的元素个数

size元素的大小


返回值


该函数返回一个指针,指向已分配的内存。如果请求失败,则返回 NULL


注意:


1.函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0

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


举个例子:

malloc不会初始化空间,cd就是 的意思

微信图片_20220415190235.png

calloc会初始化空间为0


微信图片_20220415190256.png

2.4    realloc

描述


C 库函数 void *realloc(void *ptr, size_t size) 尝试重新调整之前调用 malloc 或 calloc 所分配的 ptr 所指向的内存块的大小


声明


void *realloc(void *ptr, size_t size)


参数


ptr – 指针指向一个要重新分配内存的内存块,该内存块之前是通过调用 malloc、calloc 或 realloc 进行分配内存的。如果为空指针,则会分配一个新的内存块,且函数返回一个指向它的指针

size – 内存块的新的大小,以字节为单位。如果大小为 0,且 ptr 指向一个已存在的内存块,则 ptr 所指向的内存块会被释放,并返回一个空指针


返回值


该函数返回一个指针 ,指向重新分配大小的内存。如果请求失败,则返回 NULL


注意:


这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到 的空间


realloc在调整内存空间的时候存在两种情况

情况1:原有空间之后没有足够大的空间

情况2:原有空间之后有足够大的空间


微信图片_20220415190604.png微信图片_20220415190616.png


因为有两种情况的存在,所以我们在使用realloc函数的同时要注意检查返回的是否为空指针


#include

int main()

{

int *ptr = malloc(100);

if(ptr != NULL)

{

    //业务处理

}

else

{

    exit(EXIT_FAILURE);    

}

//扩展容量

//代码1

ptr = realloc(ptr, 1000);//这样可以吗?(如果申请失败会如何?)

// 答案是不可以,有可能会追加开辟内存失败,然后丢失原有内存

//代码2

int*p = NULL;

p = realloc(ptr, 1000);//通过一个中间变量来判断是否追加开辟内存成功

if(p != NULL)

{

ptr = p;

}

//业务处理

free(ptr);

return 0; }

版权声明:本文为CSDN博主「敲代码的布莱恩特」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/DerrickWestbrook/article/details/120483621


相关文章
|
5天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
64 9
|
26天前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
31 3
|
4天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
43 16
|
4天前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
32 8
|
7天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
32 4
|
8天前
|
存储 C语言
【数据结构】顺序表(c语言实现)(附源码)
本文介绍了线性表和顺序表的基本概念及其实现。线性表是一种有限序列,常见的线性表有顺序表、链表、栈、队列等。顺序表是一种基于连续内存地址存储数据的数据结构,其底层逻辑是数组。文章详细讲解了静态顺序表和动态顺序表的区别,并重点介绍了动态顺序表的实现,包括初始化、销毁、打印、增删查改等操作。最后,文章总结了顺序表的时间复杂度和局限性,并预告了后续关于链表的内容。
32 3
|
8天前
|
存储 关系型数据库 MySQL
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
查询服务器CPU、内存、磁盘、网络IO、队列、数据库占用空间等等信息
161 1
|
11天前
|
C语言
【c语言】动态内存管理
本文介绍了C语言中的动态内存管理,包括其必要性及相关的四个函数:`malloc`、``calloc``、`realloc`和`free`。`malloc`用于申请内存,`calloc`申请并初始化内存,`realloc`调整内存大小,`free`释放内存。文章还列举了常见的动态内存管理错误,如空指针解引用、越界访问、错误释放等,并提供了示例代码帮助理解。
24 3
|
27天前
|
编译器 程序员 C语言
深入C语言:动态内存管理魔法
深入C语言:动态内存管理魔法
|
7天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
24 0