uC/OS-II内存管理函数内最难理解的部分就是二维指针,本文以图文并茂的方式对二维指针进行了详细分析与讲解。看完本文,相信对C里面指针的概念又会有进一步的认识。

一、OSMemCreate( ) 函数中有如下语句:

OS_MEM *pmem;

INT8U *pblk;

void **plink;

INT32U i;

plink = (void **)addr; //指向所申请内存分区的起始地址

pblk = (INT8U *)addr + blksize; //所申请内存的第二个Block的起始地址

for (i = 0; i < (nblks - 1); i++) //依次申请nblks个Block,链接成单向链表

{

*plink = (void *)pblk;

plink = *plink;

pblk = pblk + blksize;

}

红色部分是初学者对本函数最难理解的部分,因为其用到了二维指针。二维指针就是指向指针的指针,他的内容是一个目标变量的地址,也就是说仍然是一个指针,对二维指针取两次内容才能取到目标变量的内容。这里先复习一下指针的知识:

int a = 5;

int *ptr;

ptr=&a; //ptr指针是地址,指向变量a所在地址。

则可以得出:

*ptr=a; //即指针ptr指向变量a所在地址,*ptr的值就是a的值5

由此类推,对于二维指针变量**plink,指针plink是地址,*plink是plink所指向的地址内的数据,不过同时这个数据也是一个指针,并且**plink是指针(*plink)所指向的地址内的数据。

一维指针所指向的地址内存放的是普通数据,如上述ptr指针所指向的地址内存放的是int型数据5。

二维指针所指向的地址内存放的是一个一维指针,如上述指针plink所指向的地址内存放的是指针*plink。

下面详细分析上述OSMemCreate函数内的语句:

1、plink = (void **)addr;

addr本来是一个一维指针,指向所申请内存分区的起始地址。本句将addr强制转换为二维指针(注意经过强制转换后addr指针本身指向的地址是没有变化的),并将addr地址值赋给plink。则plink的内存中存储的是addr的值,即plink也指向addr所指向的分区起始地址,并且这个地址内存放的内容是指针(*plink),但指针(*plink)还未指向具体的地址。在这条语句之前,这个起始地址内的数据内容是未知的(由编译器分配的)。内存分区结构见下图1所示(假设申请的内存区有4个Block)。

在这个函数当中,我们想把addr指向的二维数组,分割成大小相同的若干块,并用指针把它们链接起来,链接指针放在每个block的首地址。但由于addr是一维指针,它指向的内容不会被解释成一个地址,而是一般的内容。我们要在这些block的首地址内存放指针,所以将addr强制转换成二维指针的目的就是让编译器将addr指向的内容解释成地址,也就是一个指针。

再将addr赋值给plink(让plink去执行连接的操作),使plink与addr指向同一个地址。*plink就是取plink与addr指向地址单元的内容,而这个内容是一个指针,也就是在以前addr指向的地方放上指针*plink。

2、pblk = (INT8U *)addr + blksize;

让pblk指向所申请内存的第二个Block的起始地址,见下图2所示。

因为addr是void型的,要强制转换为INT8U型。

3、*plink = (void *)pblk; //实际上是*plink = pblk,因为pblk是INT8U型的,要强制转换为void型

在for循环内对二维指针plink执行取内容操作(其内容为指针), *plink也是一个指针了(plink指针所代表的地址的内容),将下一个block的首地址赋值给*plink,使它指向的地方改为下一个blcok开始的地址处。

起始地址内的指针*plink被赋值为pblk,所以*plink与pblk一样指向下一个blcok开始的地址处。

如图3所示。

第一个block首地址内的内容为一个指针,该指针指向下一个block首地址。这个地址内存放的就是*plink的内容**plink。

只不过我们并不需要用到这个**plink。

4、plink = (void **)pblk;

功能与plink = (void **)addr 相似,即plink也指向第二个block的起始地址,并且使这个地址内存放的是指针(*plink)。

注意:因为plink所指向的地址变了,此时pblk所指向的地址

内的内容由原来的**plink变为了*plink指针。

并且 *plink还未被赋值,则**plink值是未知的

5、pblk = pblk + blksize;

pblk不断的下移,以指向再下一个block的开始处。

pblk(new)= pblk(old)+blksize

当再次进行for循环时,重复上述过程,利用每个block首地址内的指针将每个Block链接起来组成空闲块链表。

同样,*plink被赋值后,指向pblk所指向的地址,则该地址的

内容为**plink,只不过我们并不需要取出**plink。

6、pmem->OSMemFreeList = addr; /*pmem->OSMemFreeList指向空闲块链表第一个block首地址

在完成for循环后,使pmem->OSMemFreeList指向addr,组成完整的空闲块链表。

总结:进行(void**)强制转换的目的其实就是为了把所指向的地址的内容转换成一个指针。

二、在OSMemGet( )函数内同样有一条强制转换为二维指针的指令:

void *pblk;

执行操作:pmem->OSMemFreeList =*(void **)pblk;

pblk被强制转换为二维指针,然后取出其内容*pblk,也就是pblk地址内存放的链接指针。

意味着取出pblk的内容,由于pblk被强制转换成了二维指针,所以它的内容就不是一般的值,而是一个指针(这个指针指向下一个Block首地址)。

三、在INT8U OSMemPut (OS_MEM *pmem, void *pblk) 函数内同样有类似的指令:

① *(void **)pblk = pmem->OSMemFreeList; // 将欲释放的块添加到空闲块链表最前面

② pmem->OSMemFreeList = pblk;

首先要明白pmem->OSMemFreeList是指向空闲块链表第一个block的首地址的。

语句①将pblk强制转换为二维指针后,再将pmem->OSMemFreeList赋值给pblk的内容(*pblk指针)。根据OSMemPut函数的定义,pblk是函数的形参,是欲释放的块的首地址。所以也就是将pmem->OSMemFreeList指针放入欲释放的块的首地址内,此处强制转换为二维指针的目的就是让欲释放的块的首地址内能存放指针。则这个块的首地址内的指针就是指向原先空闲块链表第一个Block的首地址的,也就是说这个块变成了空闲块链表第一个Block,实现了将释放的块添加到空闲块链表最前面的目的。

然后语句②更新pmem->OSMemFreeList指针,使其指向新释放的块的首地址,这样就保证了pmem->OSMemFreeList始终指向空闲块链表第一个Block首地址。

二维指针*(void **)的研究(uC/OS-II案例)相关推荐

  1. 最长公共子序列的C++实现---附二维指针的使用方法

    想了挺久到底第一篇在这儿的博客写什么好,刚好这两天又一次看到动态规划的LCS算法觉得还是有点意思的,就拿来写了写,第一篇博客就发它吧. #include<iostream> #includ ...

  2. C语言什么时候必须用到二级指针?(需要调用函数为一维空指针确定值的情况下,需要传入二维指针,也就是那个一维指针的指针)

    结论:需要调用函数为一维空指针确定值的情况下,需要传入二维指针,也就是那个一维指针的指针 例子:为空指针p开辟内存空间 1.不用二级指针(无法实现) #include <stdio.h> ...

  3. 【C语言进阶深度学习记录】三十 二维数组与二维指针

    文章目录 1 二维指针(指向指针的指针) 2 二维数组 3 二维数组的类型 3.2 如何动态申请二维数组 4 总结 1 二维指针(指向指针的指针) 指针的本质是变量 指针的指针是保存指针变量的地址.如 ...

  4. C++链表插入节点函数为什么要传递头节点的二维指针

    C++链表插入一个节点的代码如下: struct ListNode {int m_value;ListNode * m_next; };void addListNode(ListNode** pHea ...

  5. C++实现离散余弦变换(参数为二维指针)

    http://www.cnblogs.com/scut-linmaojiang/p/5013590.html 写在前面 到目前为止已经阅读了相当一部分的网格水印等方面的论文了,但是论文的实现进度还没有 ...

  6. C语言基础知识之define宏定义表达式,undef,内存对齐,a和a的区别,数组知识点,int (*)[10] p,二维数组参数与二维指针参数,函数指针数组,常见的内存错误及对策

    一.用define宏定义表达式 1.定义一年有多少秒: #define SEC_A_YEAR 60*60*24*365 //上述描述不可靠,没有考虑到在16位系统下把这样一个数赋给整型变量的时候可能会 ...

  7. 二维离散变换由c语言编写,C++实现离散余弦变换(参数为二维指针)(示例代码)...

    http://www.cnblogs.com/scut-linmaojiang/p/5013590.html 写在前面 到目前为止已经阅读了相当一部分的网格水印等方面的论文了,但是论文的实现进度还没有 ...

  8. c-二维数组赋值及二维指针使用

    c-二位数组赋值及二维指针使用 #include <stdio.h> void main() { int test[3][2] = {{1, 1}, {2, 2}, {3, 3}}; in ...

  9. OpenCV中图像Mat,二维指针和CxImage类之间的转换

    在做图像处理中,常用的函数接口有Opencv中的Mat图像类,有时候需要直接用二维指针开辟内存直接存储图像数据,有时候需要用到CxImage类存储图像.本文主要是总结下这三类存储方式之间的图像数据的转 ...

最新文章

  1. SAP Retail里的分类(Assortment)
  2. 高斯混合模型--GMM(Gaussian Mixture Model)
  3. Go中的Socket编程
  4. Spring Boot——游戏成就系统设计DEMO
  5. 利用python爬虫(part4)--requests模块之requests.get方法
  6. HDU 6836 Expectation(矩阵生成树 + 期望)
  7. Java的未来项目:巴拿马,织布机,琥珀和瓦尔哈拉
  8. Thinking In Java 读书笔记
  9. MySQL学习足迹记录01--SOURCE,SHOW
  10. 报表性能优化方案之多数据集实现层式报表
  11. django 类取消csrf_最新Django全套教程 半个月搞定Django
  12. 基于能量均衡高效WSN的LEACH协议改进算法
  13. ac3165无线网卡驱动linux,intel ac 3165无线网卡驱动下载v20.110.0 官方版
  14. FreeSWITCH 放音相关APP/API
  15. 9.Makefile隐含规则
  16. 软件测试员最核心的竞争力究竟是什么
  17. IPv6改造的重要性
  18. 超详细的Linux常用命令锦集,推荐收藏!!!【持续更新】
  19. js数据类型强制转换--转换为String
  20. 201903-2二十四点[20201213封笔题目]没写呢

热门文章

  1. 【测试工程师面试】面试官热衷询问的N个问题
  2. 一次PostgreSQL行估算偏差导致的慢查询分析
  3. Linux Vi 删除全部内容,删除某行到结尾,删除某段内容 的方法
  4. unity3d 数学的数学基础和辅助类
  5. 老男孩linux培训期中学生作业文档目录展示
  6. 把一个中文日期时间格式字符串转为日期时间
  7. 目标检测之RCNN,SPP-NET,Fast-RCNN,Faster-RCNN
  8. 动态规划之----最长公共子序列
  9. UVA 10196 Check The Check(模拟)
  10. 2019.07.11