【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


相关文章
|
1月前
|
存储 开发框架 .NET
"揭秘.NET内存奥秘:从CIL深处窥探值类型与引用类型的生死较量,一场关于速度与空间的激情大戏!"
【8月更文挑战第16天】在.NET框架中,通过CIL(公共中间语言)可以深入了解值类型与引用类型的内存分配机制。值类型如`int`和`double`直接在方法调用堆栈上分配,访问迅速,生命周期随栈帧销毁而结束。引用类型如`string`在托管堆上分配,堆栈上仅存储引用,CLR负责垃圾回收,确保高效且自动化的内存管理。
45 6
|
1月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
|
16天前
|
存储 大数据 C语言
C语言 内存管理
本文详细介绍了内存管理和相关操作函数。首先讲解了进程与程序的区别及进程空间的概念,接着深入探讨了栈内存和堆内存的特点、大小及其管理方法。在堆内存部分,具体分析了 `malloc()`、`calloc()`、`realloc()` 和 `free()` 等函数的功能和用法。最后介绍了 `memcpy`、`memmove`、`memcmp`、`memchr` 和 `memset` 等内存操作函数,并提供了示例代码。通过这些内容,读者可以全面了解内存管理的基本原理和实践技巧。
|
16天前
|
缓存 Linux C语言
C语言 多进程编程(六)共享内存
本文介绍了Linux系统下的多进程通信机制——共享内存的使用方法。首先详细讲解了如何通过`shmget()`函数创建共享内存,并提供了示例代码。接着介绍了如何利用`shmctl()`函数删除共享内存。随后,文章解释了共享内存映射的概念及其实现方法,包括使用`shmat()`函数进行映射以及使用`shmdt()`函数解除映射,并给出了相应的示例代码。最后,展示了如何在共享内存中读写数据的具体操作流程。
|
1月前
|
存储 缓存 编译器
Linux源码阅读笔记06-RCU机制和内存优化屏障
Linux源码阅读笔记06-RCU机制和内存优化屏障
|
1月前
|
算法 编译器 C语言
【C语言篇】猜数字游戏(赋源码)
rand函数会返回⼀个伪随机数,这个随机数的范围是在0~RAND_MAX之间,这个RAND_MAX的⼤⼩是依赖编译器上实现的,但是⼤部分编译器上是32767。
|
1月前
|
算法 安全 UED
探索操作系统的内核空间:虚拟内存管理
【7月更文挑战第50天】 在现代操作系统中,虚拟内存管理是核心功能之一,它允许操作系统高效地使用物理内存,并为应用程序提供独立的地址空间。本文将深入探讨操作系统虚拟内存管理的机制,包括分页、分段以及内存交换等关键技术,并分析它们如何共同作用以实现内存的有效管理和保护。通过理解这些原理,读者可以更好地把握操作系统的内部工作原理及其对应用程序性能的影响。
|
1月前
|
存储 程序员 C语言
【C语言】动态内存管理
【C语言】动态内存管理
|
1月前
|
存储 数据可视化 数据安全/隐私保护
【C语言】C语言-成绩管理系统(管理员+教师+学生 源码)【独一无二】
【C语言】C语言-成绩管理系统(管理员+教师+学生 源码)【独一无二】