共4条
1/1 1 跳转至页
嵌入式C指针应用详解
1.指针固定地址直接访问:
#define rEXTINT0 (*(volatile unsigned *)0x56000088) //External interrupt control register 0
嵌入式系统编程,要求程序员能够利用C语言访问固定的内存地址。既然是个地址,那么按照C语言的语法规则,这个表示地址的量应该是指针类型。所以,知道要访问的内存地址后,比如0x56000088,
第一步是要把它强制转换为指针类型
(unsigned char *)0x56000088,s3c2410的rRTCCON是单字节访问的,所以0x56000088强制转换为指向unsigned char类型。
volatile(可变的)这个关键字说明这变量可能会被意想不到地改变,这样编译器就不会去假设这个变量的值了。这种“意想不到地改变”,不是由程序去改变,而是由硬件去改变——意想不到。
第二步,对指针变量解引用,就能操作指针所指向的地址的内容了
*(volatile unsigned char *)0x57000043
第三步,小心地把#define宏中的参数用括号括起来,这是一个很好的习惯。
(*(volatile unsigned *)0x56000088)
((void (*)(void)) (function[0]) )()功能分析
根据 Andrew Koening在他的《C 陷阱与缺陷》里对(*(void (*)( ) )0)( )的分析得到以下结论。
1.如何声明一个变量?
float *g():g是一个函数,他的返回值是一个指针,该指针指向一个float数。
float (*h)(float):h是一个指针,该指针指向一个函数,这个函数的返回值是float类型的,这个函数的参数是float类型。
2.得到类型转换符。
把声明中的变量名和声明末尾的分号去掉,把剩下的作为一个整体用括号封装起来,就是一个类型转换符。
(1)float f;去掉变量名和分号,得到(float),即得到一个类型转换符
(2)float (*h)(float);去掉变量名和分号,得到(float(*)(float))这样一个类型转换符。
将(2) 中的float换成void就得到我们这里的(void (*)(void)),function[0]是一个指针数组中的一个元素,即“Test_Zdma0”,“Test_Zdma0”是一个函数名,它表示这个函数的地址,那么(void (*)(void)) (function[0])意思就是把Test——Zdma0这个地址转换成一个可以调用的函数指针。
3.调用函数指针。
假定fp是一个函数指针,那么*fp就是该指针指向的函数,那么调用它的方法就是(*fp)() 。标准C允许将上式简写为fp(),这种写法只是一种简写形式。
将fp换成上面的(void (*)(void)) (function[0])那么函数调用就得到(*((void (*)(void)) (function[0])))(),他的简写形式就是我们要分析的((void (*)(void)) (function[0]) )().
这个语句的意思其实就是,调用Test_Zdma0()这个函数,只是把它放到数组里,何以根据自己的需要方便调用。它就是根据Andrew Koening的(*(void (*)( ) )0)( )这个语句得来的,这里的function[0]和0的意义相同。
3.二维数组与指针
从存储的角度来说,二维数组在内存中的存储,是按照先行后列依次存放的。从内存的角度看,可以这样说,二维数组其实就是一个一维数组,在内存中没有二维的概念。
如果把二维数组的每一行看成一个整体(如一个字或结构体占多个字节),即看成一个数组中的一个元素,那么整个二维数组就是一个一维数组,它以每一行作为它的元素,这个应该很好理解。
第一,我们来详细介绍二维数组与指针的关系。
首先定义个二维数组 array[3][4],p 为指向数组的指针。
若p=array[0],此时p指向的是二维数组第一行的首地址,则 p+i 将指向array[0]数组中的元素array[0]。由以上所介绍的二维数组在内存中的存储方式可知,对数组中的任一元素array[j] ,其指针的形式为:p+i*N+j (N为每一行的长度)。 元素相应的指针表示法为:*(p+i*N+j) ,下标表示法为:p[i*N+j] 。
For Example:
array[4][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};
int * p=array[0];
数组array有四个元素,分别为array[0],array[1],array[2],array[3],每个元素为包含3个元素的一维数组,
如array[0]的3个元素为 array[0][0],array[0][1],array[0][2]。
元素array[2][2]对应指针为:array+2*3+2,
指针表示法为:*(array+2*3+2) ,
下标表示法为:array[2*3+2] 。
特别注意:虽然 array[0] 与 array 都是数组首地址,但两者指向的对象不同,这点要非常明确。array[0] 是一维数组的名字,它指向的是一维数组array[0]的首地址,所以 *array[0]与array[0][0]为同个值。而 array 是二维数组的名字,它指向的是所属元素的首地址,其每个元素为一个行数组。它是以‘行’来作为指针移动单位的,如arraya+i 指向的是第 i 行。对 array 进行 * 运算,得到的是一维数组 array[0] 的首地址的值,所以 *array 与 array[0] 为同个值。如果定义 int* p,p为指int类型的指针,指向int 类型,而不是一个地址。故以下操作 :p=array[0] (正确) ,p=array (错误) 。这点要非常注意!
第二,看看如何用数组名作地址表示其中元素。
我们知道,对二维数组array ,array[0] 由 array指向,故*array 与array[0] 是相同的,依次类推可得 array 由array+i 指向,*(array+i) 与array是相同的。 因此,对于数组元素 array[j] ,用数组名表示为 *(*(array+i)+j) ,指向该元素的指针为 *(array+i)+j 。
注意:数组名虽然是地址,但与指向数组的指针性质不同。指针变量可以随时改变其所指向对象,而数组名不可以,一旦被定义,就不能通过赋值使其指向另外一个数组,但是在Java中则可以。
第三,顺便了解一下不太常用的‘行数组指针’。
我们知道,对于二维数组array[4][3],与int* p 。二维数组名array 不能直接赋值给p。原因前面已讲过,两者的对象性质不同。 在C语言中,我们可以通过定义一个行数组指针,使得这个指针与二维数组名具有同样的性质,实现它们之间可以直接赋值。行数组指针定义如下:
数据类型 (*指针变量名)[二维数组列数];
int (*p)[3]; 定义一个数组指针,指向的数组具有三个元素。
它表示,数组 *p 具有三个int类型元素,分别为 (*p)[0] , (*p)[1] , (*p)[2] ,即 p指向的是具有三个int类型的一维数组,也就是说,p为行指针。此时,以下运算 p=array 是正确的。当prt指向a数组的开头时,可以通过以下形式来引用a[j]:
(1) *(prt+j) (2) *(*(prt+i)+j) (3)(*(prt+i))[j] (4) prt[j] 在这里,prt是个指针变量,它的值可变,而a是一个常量。
关键词: 嵌入式 指针 应用 详解 地址 这个 类型 指向
共4条
1/1 1 跳转至页
回复
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
【换取逻辑分析仪】自制底板并驱动ArduinoNanoRP2040ConnectLCD扩展板被打赏47分 | |
【分享评测,赢取加热台】RISC-V GCC 内嵌汇编使用被打赏38分 | |
【换取逻辑分析仪】-基于ADI单片机MAX78000的简易MP3音乐播放器被打赏48分 | |
我想要一部加热台+树莓派PICO驱动AHT10被打赏38分 | |
【换取逻辑分析仪】-硬件SPI驱动OLED屏幕被打赏36分 | |
换逻辑分析仪+上下拉与多路选择器被打赏29分 | |
Let'sdo第3期任务合集被打赏50分 | |
换逻辑分析仪+Verilog三态门被打赏27分 | |
换逻辑分析仪+Verilog多输出门被打赏24分 | |
【分享评测,赢取加热台】使用8051单片机驱动WS2812被打赏40分 |