C语言进阶第七篇【动态存储和柔性数组】(下)

简介: C语言进阶第七篇【动态存储和柔性数组】(下)

6. 几个经典的笔试题


❤️6.1  题目1:


bea57a32d5c04415a698e4721a6941b5.png


问题1:


str传给GetMemory函数的时候是值传递,所以GetMemory函数的形参p是str的一份临时拷贝,在GetMemory函数内部动态申请空间的地址,存放在p中,不会影响外边str,所以当GetMemory函数返回之后,str依然是NULL。所以strcpy拷贝会失败。


问题2:


没有free释放,当GetMemory函数返回之后,形参p销毁,使得动态开辟的100个字节存在内存泄漏。


⭐️修改方法1:

925838efd8c0495a94565f3fe814d729.png


我们就改成传址调用,利用二级指针接收!这样对形参的改变就会影响实参,此时把动态申请的空间交给指针p,其实就是给str!最后用完还要进行释放!


⭐️修改方法2:


0082729952d9412297f97117fca57145.png

不用传址调用怎么办呢?这就需要利用return指针返回来,这时我们还是用一级指针接收!因为是动态开辟的空间实际上是在堆区上开辟的,需要手动才销毁;我们把这个地址利用return带回来,然后在利用一个指针就接收就可以了!


比如:这种方法就可以应用在带头循环双链表中给头结点开辟空间,就可以利用return返回值的方式,避免使用二级指针!


❤️6.2  题目2:

eacb90f56ada42ceb20cf03a23360c31.png


问题:


同样是利用return返回值的方式,这里为什么就不行了呢?GetMemory函数内部创建的数组是在栈区上创建的,出了函数,p数组的空间就还给了操作系统,返回的地址是没有实际意义的,如果通过返回的地址,去访问内存就是非法访问内存!


❤️6.4  题目4:


e0aced74600948c1aa1eea937170c0db.png

问题:


看着好像是没什么问题,是传址调用,二级指针接收;主要就是,没有释放空间!free(str),str = NULL!


❤️6.4  题目4:


79733948626d46af934fdc0662c1bdae.png


问题:


str已经释放了,下面就不能在访问,在访问就属于非法访问内存;为了避免这种错误发生:free过后一定要手动置空 str=NULL


❤️6.5  题目5:


9f0fe59a031b4ccea3593b8838a5f2b0.png

问题:


(1)x是局部变量,在栈区上创建,出了这个范围,就把内存释放了,return(&x)没有实际意义!

(2)指针ptr才开始没有初始化,是野指针,里面放的随机值!


❤️6.6  经典例题补充

5a05694aa1f54a85a68126386c44c9ce.png


解析:


(1)对于#define定义符号,相当于替换,所以是int*a,b===>*说明a是指针,类型是整型;对于b只能说明是整型!所以如果我们要是一下子定义两个指针int*a,*b要写成这种形式。

(2)typedef是类型重定义,代表着一种类型;把int*起个别名叫int_ptr;所以c,d都是指针类型


7. C/C++程序的内存开辟

1fdf3bfecf3b431d9ec8c10eb10f5ef6.png


C/C++程序内存分配的几个区域:

1. 栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。

2. 堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。分配方式类似于链表。

3. 数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。

4. 代码段:存放函数体(类成员函数和全局函数)的二进制代码。

补充:static关键字修饰局部变量的例子解释

实际上普通的局部变量是在栈区分配空间的,栈区的特点是在上面创建的变量出了作用域就销毁。但是被static修饰的变量存放在数据段(静态区),数据段的特点是在上面创建的变量,直到程序结束才销毁所以生命周期变长1


8. 柔型数组

C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。

8.1 柔性数组的特点

(1)结构中的柔性数组成员前面必须至少一个其他成员。


(2)sizeof 返回的这种结构大小不包括柔性数组的内存。

(3)包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。


(4)柔型数组开辟的空间都是在堆上开辟的!


❤️例:

8b9ae902e19c43ff9eff30efa00d6b46.png



8.2 柔性数组的使用

❤️计算大小


03085e78d4ee47b39bfb30c49ab3050a.png




❤️柔性数组的动态创建



7efa7a240feb4ab6aa759edeabc2895e.png

(1)对于柔型数组的动态开辟空间,是用结构体指针(struct S* ps)进行接收;对于大小主要包括两部分:


       一是n的大小创建,我们就用sizeof(struct S),这算的就是除柔性数组以外的大小;


       二是柔型数组的动态开辟;其实就是开辟10个整型,我们就用10*sizeof(int);


(2)成功开辟后就使用,通过指针ps来查看内存发现已经被赋值;


(3)用完了,我们就用realloc进行扩容,这也就体现了柔性数组的特点,大小是可变的;我们用一个新的指针ptr进行接收,扩容成功后;把新指针ptr赋值给旧指针ps;


(4)最后在进行ps指针的释放,free(ps),ps = NULL ;


❤️利用指针实现柔型数组的功能

为了保证和柔性数组一样的性质,都是在堆上开辟的!我们所有的都需要用malloc开辟空间;柔型数组是一个malloc整体一块开辟的;用指针代替柔性数组,就要分开开辟:一个开辟除指针以外的大小的而空间;一个开辟指针所需大小的空间!


8f1c6d5c63dc4bba839a0fdc8f79a36c.png


(1)利用指针代替柔型数组;实际上就是从一步到两步的过程;柔性数组只需要一次malloc;而指针形式就需要两次malloc;两次malloc就需要两次free;


(2)第一次malloc是动态创建除指针以外所需的空间;第二次malloc是动态创建指针所需大小的空间;


(3)成功开辟后就使用,通过指针ps->arr来查看内存发现已经被赋值;


(4)用完了,我们就用realloc进行扩容,我们就只需要对指针部分进行扩容;我们用一个新的指针ptr进行接收,扩容成功后;把新指针ptr赋值给旧指针ps->arr;


(5)最后在进行ps->arr指针和ps指针的释放,free(ps->arr),ps->arr = NULL ;free(ps),ps = NULL


9. 习题补充

❤️9.1 习题1

321e0b231e6841b18cd63338d8b6dd01.png


看着图形很简单,就打印两个对角线的位置;但是也要从中找到不变的量:一个对角线是i == j这个很容易看出来,另一条对角线呢?其实就是 i+j = n-1!

95bc6b680617487c96e2ecdca5d0c796.png



❤️9.2 习题2


01ef9bc1f5bc4986b942732474b7e932.png


首先我们要理清楚规律:1、3、5、7、8、10、12 是31天;4、6、9、11是30天;如果是平年2月份是28天,如果是闰年二月份是29天!31,28,31,30,31,30,31,31,30,31,30,31!


我们发现对于月份好像也没什么规律,都是有奇数也有偶数;数据也不多,不妨就写成数组的形式!

63563d6087484e85b5fb7a3358ea8ff5.png



❤️9.3 习题3

cca8a295e38e46068316822ddf1f88fe.png


我们在插入的时候,就面临一个问题:从前往后插还是从后往前插?如果从前往后插我们移动数据时就会造成数据的覆盖;所以我们就从后往前插入!

b17cfb47578c403ab0c47ef6a44608dd.png

相关文章
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
379 0
|
2月前
|
存储 编译器 C语言
C语言存储类详解
在 C 语言中,存储类定义了变量的生命周期、作用域和可见性。主要包括:`auto`(默认存储类,块级作用域),`register`(建议存储在寄存器中,作用域同 `auto`,不可取地址),`static`(生命周期贯穿整个程序,局部静态变量在函数间保持值,全局静态变量限于本文件),`extern`(声明变量在其他文件中定义,允许跨文件访问)。此外,`typedef` 用于定义新数据类型名称,提升代码可读性。 示例代码展示了不同存储类变量的使用方式,通过两次调用 `function()` 函数,观察静态变量 `b` 的变化。合理选择存储类可以优化程序性能和内存使用。
158 82
|
5月前
|
C语言
指针进阶(C语言终)
指针进阶(C语言终)
|
1月前
|
存储 C语言
深入C语言内存:数据在内存中的存储
深入C语言内存:数据在内存中的存储
|
2月前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
401 8
|
1月前
|
存储 C语言
C语言中的浮点数存储:深入探讨
C语言中的浮点数存储:深入探讨
|
2月前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
2月前
|
存储 机器学习/深度学习 C语言
数据结构基础详解(C语言): 树与二叉树的基本类型与存储结构详解
本文介绍了树和二叉树的基本概念及性质。树是由节点组成的层次结构,其中节点的度为其分支数量,树的度为树中最大节点度数。二叉树是一种特殊的树,其节点最多有两个子节点,具有多种性质,如叶子节点数与度为2的节点数之间的关系。此外,还介绍了二叉树的不同形态,包括满二叉树、完全二叉树、二叉排序树和平衡二叉树,并探讨了二叉树的顺序存储和链式存储结构。
|
2月前
|
存储 算法 C语言
C语言手撕数据结构代码_顺序表_静态存储_动态存储
本文介绍了基于静态和动态存储的顺序表操作实现,涵盖创建、删除、插入、合并、求交集与差集、逆置及循环移动等常见操作。通过详细的C语言代码示例,展示了如何高效地处理顺序表数据结构的各种问题。
|
2月前
|
存储 缓存 程序员
c语言的存储类型-存储类
本文详细介绍了C语言中的存储类型及其分类,包括基本类型(如整型、浮点型)和复合类型(如数组、结构体)。重点讲解了不同存储类别(`auto`、`static`、`register`、`extern`、`typedef`、`volatile`、`const`)的特点及应用场景,并展示了C11/C99引入的新关键字(如`_Alignas`、`_Atomic`等)。通过示例代码解释了每个存储类别的具体用法,帮助读者更好地理解和运用这些概念。