指向数组元素的指针和运算规律

所谓指向数组元素的指针,其实质照样变量的指针。由于数组中的每一个元素,其实都可以直接算作是一个变量,所以指向数组元素的指针,也就是变量的指针。
指向数组元素的指针不难,但很常用。我们用程序来说明会比拟直不雅一些。

unsigned char number[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; unsigned char *p;

假如我们写 p = &number[0];那么指针 p 就指向了 number 的第 0 号元素,也就是把number[0]的地址赋值给了 p,同理,假如写 p = &number[1];p 就指向了数组 number 的第 1号元素。p = &number[x];个中 x 的取值规模是 0~9,就表现 p 指向了数组 number 的第 x 号元素。指针自身,也可以停止几种复杂的运算,这几种运算关于数组元素的指针来说使用最多。

  1. 比拟运算。比拟的前提是两个指针指向同品种型的对象,比方两个指针变量 p 和 q它们指向了具有同种数据类型的数组,那它们可以停止 <,>,>=,<=,==等关系运算。假如 p==q 为真的话,表现这两个指针指向的是统一个元素。

  2. 指针和整数可以直接停止加减运算。比方照样上边我们谁人指针 p 和数组 number,假如 p = &number[0],那么 p+1 就指向了 number[1],p+9 就指向了 number[9]。当然了,假如 p = &number[9],p-9 也就指向了 number[0]。

  3. 两个指针变量在必定前提下可以停止减法运算。如 p = &number[0]; q = &number[9];那么 q-p 的后果就是 9。然则这个中央人人要特殊留意,这个 9 代表的是元素的个数,而不是真正的地址差值。假如我们的 number 的变量类型是 unsigned int 型,占 2 个字节,q-p 的后果仍然是 9,由于它代表的是数组元素的个数。


在数组元素指针这里还有一种状况,就是数组名字其实就代表了数组元素的首地址,也就是说:

p = &number[0]; p = number;

这两种表达方法是等价的,因而以下几种表达方式和内容需求人人非分特别留意一下。
依据指针的运算规矩,p+x 代表的是 number[x]的地址,那么 number+x 代表的也是number[x]的地址。或许说,它们指向的多是 number 数组的第 x 号元素。
*(p+x)和*(number+x)都表现 number[x]。
指向数组元素的指针也可以表现成数组的方式,也就是说,许可指针变量带下标,即 p[i]和*(p+i)是等价的。然则为了防止混杂与标准起见,这里我们建议人人不要写成前者,而一概采取后者的写法。但假如看到他人那么写,也晓得是怎样回事即可。
二维数组元素的指针和一维数组相似,需求引见的内容不多。假设如今一个指针变量 p和一个二维数组 number[3][4],它的地址的表达方法也就是 p=&number[0][0],有一个中央要留意,既然数组名代表了数组元素的首地址,那么也就是说 p 和 number 多是指数组的首地址。对二维数组来说,number[0],number[1],number[2]都可以算作是一维数组的数组名字,所以 number[0]等价于 &number[0][0], number[1]等价于 &number[1][0], number[2]等价于&number[2][0]。加减运算和一维数组是相似的,不再胪陈。

指向数组元素指针的实例

在 C 言语里边,sizeof()可以用来获取括号内的对象所占用的内存字节数,固然它写作函数的方式,但它并不是一个函数,而是 C 言语的一个症结字,sizeof()全体在程序代码中就相当于一个常量,也就是说这个获取操作是在程序编译的时分停止的,而不是在程序运转的时分停止。这是一个实践编程中很有效的症结字,灵敏应用它可认为程序带来更好的可读性、易保护性和可移植性,在后续的例程进修中将会渐渐有所领会的。
sizeof()括号中可所以变量名,也可所以变量类型名,其后果是等效的。而其更大的用途是与数组名搭配运用,如许可以获取全部数组占用的字节数,就不必本人入手盘算了,可以防止毛病,而假如日后改动了数组的维数时,也不需求再到履行代码中逐一修正,便于程序的保护和移植。
下面我们供给了一个复杂的串口演示例程,可以体验一下指针和 sizeof()的用法。例程起首接纳上位机下发的敕令,依据敕令值辨别把分歧数组的数据回发给上位机,程序还用到了指针的自增运算,也就是+1 运算,人人可以仔细思索一下指针 ptrTxd 在串口发送的进程中的指向是若何变更的。在上位机串口调试助手平分别下发 1、2、3、4,就会失掉分歧的数组回发,留意这里都用十六进制发送和十六进制显示。
此外,这个程序还使用到一个小技能,人人要学会运用。我们前边讲了串口发送中缀标记位 TI 是硬件置位,软件清零的。平日来讲,我们想一次发送多个数据的时分,就需求把第一个字节写入 SBUF,然后再等候发送中缀,在后续中缀中再发送残剩的数据,如许我们的数据发送进程就被拆分到了两个中央——主轮回内和中缀效劳函数内,无疑就使得程序构造变得零碎了。这个时分,为了使程序构造尽量紧凑,在启动发送的时分,不是向 SBUF 中写入第一个待发的字节,而是直接让 TI=1,留意,这时分会立时进入串口中缀,由于中缀标记地位 1 了,然则串口线上并没有发送任何数据,于是,我们一切的数据发送都可以在中缀中停止,而不必再分为两局部了。人人可以在程序中领会一下这个技能的益处。

纯文本复制
#include 
 bit cmdArrived = 0; //敕令抵达标记,即接纳到上位机下发的敕令 unsigned char cmdIndex = 0; //敕令索引,即与上位机商定好的数组编号 unsigned char cntTxd = 0; //串口发送计数器 unsigned char *ptrTxd; //串口发送指针 unsigned char array1[1] = {1}; unsigned char array2[2] = {1,2}; unsigned char array3[4] = {1,2,3,4}; unsigned char array4[8] = {1,2,3,4,5,6,7,8}; void ConfigUART(unsigned int baud); void main(){ EA = 1; //开总中缀 ConfigUART(9600); //设置装备摆设波特率为 9600 while (1){ if (cmdArrived){ cmdArrived = 0; switch (cmdIndex){ case 1: ptrTxd = array1; //数组 1 的首地址赋值给发送指针 cntTxd = sizeof(array1); //数组 1 的长度赋值给发送计数器 TI = 1; //手动方法启动发送中缀,处置数据发送 break; case 2: ptrTxd = array2; cntTxd = sizeof(array2); TI = 1; break; case 3: ptrTxd = array3; cntTxd = sizeof(array3); TI = 1; break; case 4: ptrTxd = array4; cntTxd = sizeof(array4); TI = 1; break; default: break; } } } } /* 串口设置装备摆设函数,baud-通讯波特率 */ void ConfigUART(unsigned int baud){ SCON = 0x50; //设置装备摆设串口为形式 1 TMOD &= 0x0F; //清零 T1 的掌握位 TMOD |= 0x20; //设置装备摆设 T1 为形式 2 TH1 = 256 - (11059200/12/32)/baud; //盘算 T1 重载值 TL1 = TH1; //初值等于重载值 ET1 = 0; //制止 T1 中缀 ES = 1; //使能串口中缀 TR1 = 1; //启动 T1 } /* UART 中缀效劳函数 */ void InterruptUART() interrupt 4{ if (RI){ //接纳到字节 RI = 0; //清零接纳中缀标记位 cmdIndex = SBUF; //接纳到的数据保管到敕令索引中 cmdArrived = 1;//设置敕令抵达标记 } if (TI){ //字节发送终了 TI = 0; //清零发送中缀标记位 if (cntTxd > 0){ //有待发送数据时,持续发送后续字节 SBUF = *ptrTxd; //收回指针指向的数据 cntTxd--; //发送计数器递加 ptrTxd++; //发送指针递增 } } }