二维指针*(void **)的研究(uC/OS-II案例) 《转载》
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案例) 《转载》的更多相关文章
- C++实现离散余弦变换(参数为二维指针)
C++实现离散余弦变换(参数为二维指针) 写在前面 到目前为止已经阅读了相当一部分的网格水印等方面的论文了,但是论文的实现进度还没有更上,这个月准备挑选一些较为经典的论文,将其中的算法实现.在实现论文 ...
- C语言里的指针探析——type *name[] 在函数参数里面,是一个二维指针
type *name[] 在函数参数里面声明和不在函数里面声明其实不一样. type *name[] 如果在函数参数里声明,则name 是一个二维指针,并不是一个指针数组,而如果不在函数参数里声明,则 ...
- 通过数组初始化链表的两种方法:指向指针的引用node *&tail和指向指针的指针(二维指针)node **tail
面试高频题:单链表的逆置操作/链表逆序相关文章 点击打开 void init_node(node *tail,char *init_array) 这样声明函数是不正确的,函数的原意是通过数组初始化链表 ...
- 最长公共子序列的C++实现---附二维指针的使用方法
想了挺久到底第一篇在这儿的博客写什么好,刚好这两天又一次看到动态规划的LCS算法觉得还是有点意思的,就拿来写了写,第一篇博客就发它吧. #include<iostream> #includ ...
- 【视频开发】OpenCV中Mat,图像二维指针和CxImage类的转换
在做图像处理中,常用的函数接口有OpenCV中的Mat图像类,有时候需要直接用二维指针开辟内存直接存储图像数据,有时候需要用到CxImage类存储图像.本文主要是总结下这三类存储方式之间的图像数据的转 ...
- uC/OS II原理分析及源码阅读(一)
uC/OS II(Micro Control Operation System Two)是一个可以基于ROM运行的.可裁减的.抢占式.实时多任务内核,具有高度可移植性,特别适合于微处理器和控制器,是和 ...
- 【原创】uC/OS II 任务切换原理
今天学习了uC/OS II的任务切换,知道要实现任务的切换,要将原先任务的寄存器压入任务堆栈,再将新任务中任务堆栈的寄存器内容弹出到CPU的寄存器,其中的CS.IP寄存器没有出栈和入栈指令,所以只能引 ...
- 【小梅哥SOPC学习笔记】NIOS II处理器运行UC/OS II
SOPC开发流程之NIOS II 处理器运行 UC/OS II 这里以在芯航线FPGA学习套件的核心板上搭建 NIOS II 软核并运行 UCOS II操作系统为例介绍SOPC的开发流程. 第一步:建 ...
- 【C语言】二维指针做形参
转自:http://hi.baidu.com/gpmzccqceabimqq/item/f499f057aa1520404eff208b 关键: 传入时强制类型转换 + 使用时自己手工寻址 今天写程序 ...
随机推荐
- Grunt Server:Fatal error: Port 35729 is already in use by another process.
提示35729端口被占用,使用lsof命令进行查看: y@y:yo-test$ lsof -i : COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME ...
- LeetCode_Decode Ways
A message containing letters from A-Z is being encoded to numbers using the following mapping: 'A' - ...
- JQuery 插件 - 弹窗:BlockUI
JQuery的弹窗插件,网上实在有很多做的比较好的,比如artDialog.layer,甚至EasyUI等等.这些在效果上做的非常好.但我觉得一个小小的弹窗提示,没有必要引用这些(其实是有点大材小用了 ...
- less.css基础学习,陆续更新中
//基础//概念:动态样式语言,有很多语言的特性:变量,函数,运算等 //变量:通过一个简单的@+字母,数字下划线等,但不能以数字开头,不能关键字,保留字等//注意less.css是全局变量,除在函数 ...
- lesson5:利用jmeter来压测消息队列(activemq)
本文讲述了利用jmeter来压测消息队列,其中消息队列采用apache的activemq,jmeter本身是支持符合jms标准消息队列的压测,由于jmeter的官方sampler配置比较复杂,本文直接 ...
- android 解析json数据格式
json数据格式解析我自己分为两种: 一种是普通的,一种是带有数组形式的: 普通形式的:服务器端返回的json数据格式如下: {"userbean":{"Uid" ...
- 一个安全测试的CheckList
转自:http://hi.baidu.com/dontcry/item/90c2bc466558c217886d1075 不登录系统,直接输入登录后的页面的URL是否可以访问: 不登录系统,直接输入下 ...
- USB通讯协议之深入理解
0. 基本概念 一个[传输](控制.批量.中断.等时):由多个[事务]组成: 一个[事务](IN.OUT.SETUP):由一多个[Packet]组成. USB数据在[主机软件]与[USB设备特定的端点 ...
- vs开发常用快捷键
Ctrl+K+D:快速对齐代码///按D的时候K快速弹起 如果出现语法错误则无法对齐Ctrl+Z:撤销Ctrl+S:保存Ctrl+J:快速弹出智能提示Shift+End.Shift+Home//快速选 ...
- OD: Exploit Me - Overwrite Nearby Varible
实验代码: #include<stdio.h> #include<string.h> #define PASSWORD "1234567" int veri ...