多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
【61.1 指针与批量数组的关系。】 指针和批量数据的关系,更像领导和团队的关系,领导是团队的代表,所以当需要描述某个团队的时候,为了表述方便,可以把由N个人组成的团队简化成该团队的一个领导,用一个领导来代表整个团队,此时,领导就是团队,团队就是领导。指针也一样,指针一旦跟某堆数据“绑定”了,那么指针就是这堆数据,这堆数据就是该指针,所以在很多PC上位机的项目中,往往也把指针称呼为“句柄”,字面上理解,就是一句话由N个文字组成,而“句柄”就是这句话的代表,实际上“句柄”往往是某一堆资源的代表。不管是把指针比喻成“领导”、“代表”还是“句柄”,指针在这里都有“中间站”这一层含义。 【61.2 指针在批量数据的“中转站”作用。】 指针在批量数据处理中,主要是能节省代码容量,而且是非常直观的节省代码容量。为什么能节省代码容量?是因为可以把某些重复性的具体实现的功能封装成指针来操作,请看下面的例子: 程序要求:根据一个选择变量Gu8Sec的值,要从三堆数据中选择对应的一堆数据放到数组Gu8Buffer里。当Gu8Sec等于1的时候选择第1堆,等于2的时候选择第2堆,等于3的时候选择第3堆。也就是“三选一”。 第1种实现的方法:没有用指针,最原始的处理方式。如下: code unsigned char Cu8Memory\_1\[3\]={1,2,3}; //第1堆数据 code unsigned char Cu8Memory\_2\[3\]={4,5,6}; //第2堆数据 code unsigned char Cu8Memory\_3\[3\]={7,8,9}; //第3堆数据 unsigned char Gu8Sec=2; //选择的变量 unsigned char Gu8Buffer\[3\]; //根据变量来存放对应的某堆数据的数组 unsigned char i; //for循环用到的变量i switch(Gu8Sec) //根据此选择变量来切换到对应的操作上 { case 1: //第1堆 for(i=0;i<3;i++) //第1次出现for循环,用来实现“赋值”的“搬运数据”的动作。 { Gu8Buffer\[i\]=Cu8Memory\_1\[i\]; } break; case 2: //第2堆 for(i=0;i<3;i++) //第2次出现for循环,用来实现“赋值”的“搬运数据”的动作。 { Gu8Buffer\[i\]=Cu8Memory\_2\[i\]; } break; case 3: //第3堆 for(i=0;i<3;i++) //第3次出现for循环,用来实现“赋值”的“搬运数据”的动作。 { Gu8Buffer\[i\]=Cu8Memory\_3\[i\]; } break; } 分析:上述程序中,没有用到指针,出现了3次for循环的“赋值”的“搬运数据”的动作。 第2种实现的方法:用指针作为“中间站”。如下: code unsigned char Cu8Memory\_1\[3\]={1,2,3}; //第1堆数据 code unsigned char Cu8Memory\_2\[3\]={4,5,6}; //第2堆数据 code unsigned char Cu8Memory\_3\[3\]={7,8,9}; //第3堆数据 unsigned char Gu8Sec=2; //选择的变量 unsigned char Gu8Buffer\[3\]; //根据变量来存放对应的某堆数据的数组 unsigned char i; //for循环用到的变量i const unsigned char \*pCu8; //引入一个指针作为“中间站” switch(Gu8Sec) //根据此选择变量来切换到对应的操作上 { case 1: //第1堆 pCu8=&Cu8Memory\_1\[0\]; //跟第1堆数据“绑定”起来。 break; case 2: //第2堆 pCu8=&Cu8Memory\_2\[0\]; //跟第2堆数据“绑定”起来。 break; case 3: //第3堆 pCu8=&Cu8Memory\_3\[0\]; //跟第3堆数据“绑定”起来。 break; } for(i=0;i<3;i++) //第1次出现for循环,用来实现“赋值”的“搬运数据”的动作。 { Gu8Buffer\[i\]=\*pCu8; //把“指针所存的地址的数据”赋值给数组 pCu8++; //“指针所存的地址”自加1,为下一个数据的“赋值”的“搬运”作准备。 } 分析:上述程序中,用到了指针作为中间站,只出现了1次for循环的“赋值”的“搬运数据”的动作。对比之前第1种方法,在本例子中,用了指针之后,程序代码看起来更加高效简洁清爽省容量。在实际项目中,数据量越大的时候,指针这种“优越性”就越明显。 【61.3 指针在书写上另外两种常用写法。】 刚才61.2处第2个例子中,有一段代码如下: for(i=0;i<3;i++) //第1次出现for循环,用来实现“赋值”的“搬运数据”的动作。 { Gu8Buffer\[i\]=\*pCu8; //把“指针所存的地址的数据”赋值给数组 pCu8++; //“指针所存的地址”自加1,为下一个数据的“赋值”的“搬运”作准备。 } 很多高手,喜欢把上面for循环内部的那两行代码简化成一行代码,如下: for(i=0;i<3;i++) //第1次出现for循环,用来实现“赋值”的“搬运数据”的动作。 { Gu8Buffer\[i\]=\*pCu8++; //先把“数据”赋值给数组,然后“指针所存的地址”再自加1。 } 上面这种写法也是合法的,而且在高手的代码中常见,据说也是最高效的写法。还有一种是利用“指针的偏移地址”的写法,我常用这种写法,因为感觉这种写法比较直观,而且跟数组的书写很像。如下: for(i=0;i<3;i++) //第1次出现for循环,用来实现“赋值”的“搬运数据”的动作。 { Gu8Buffer\[i\]=pCu8\[i\]; //这类是“偏移地址”的写法,i在这里相当于指针的偏移地址。 } 这种写法也是跟前面那两种写法在程序实现的功能上是一样的,是等效的,我常用这种写法。 【61.4 指针的“地址自加法”和“地址偏移法”的差别。】 刚才61.3处讲了3个例子,其中前面的两个例子都是属于“地址自加法”,而最后的那一个是属于“地址偏移法”。它们的根本差别是:“地址自加法”的时候,“指针所存的地址”是变动的;而“地址偏移法”的时候,“指针所存的地址”是不变的,“指针所存的地址”的“不变”的属性,就像某个原点,原点再加上偏移,就可以寻址到某个新的RAM地址所存的数据。例子如下: 第1种:“地址自加法”: pCu8=&Cu8Memory\_2\[0\]; //假设赋值后,此时“指针所存的地址”是RAM的地址4。 for(i=0;i<3;i++) { Gu8Buffer\[i\]=\*pCu8++; //先把“数据”赋值给数组,然后“指针所存的地址”再自加1。 } 分析:上述代码,等程序执行完for循环后,指针所存的地址还是RAM地址4吗?不是。因为它是变动的,经过for循环,“指针所存的地址”自加3次后,此时“所存的RAM地址”从原来的4变成了7。 第2种:“地址偏移法”: pCu8=&Cu8Memory\_2\[0\]; //假设赋值后,此时“指针所存的地址”是RAM的地址4。 for(i=0;i<3;i++) { Gu8Buffer\[i\]=pCu8\[i\]; //这类是“偏移地址”的写法,i在这里相当于指针的偏移地址。 } 分析:上述代码,等程序执行完for循环后,指针所存的地址还是RAM地址4吗?是的。因为它存的地址是不变的,变的只是偏移地址i。此时“指针所存的地址”就像“原点”一样具有“绝对地址”的“参考点”的属性。 【61.5 例程练习和分析。】 现在编一个练习程序。 /\*---C语言学习区域的开始。-----------------------------------------------\*/ code unsigned char Cu8Memory\_1\[3\]={1,2,3}; //第1堆数据 code unsigned char Cu8Memory\_2\[3\]={4,5,6}; //第2堆数据 code unsigned char Cu8Memory\_3\[3\]={7,8,9}; //第3堆数据 unsigned char Gu8Sec=2; //选择的变量 unsigned char Gu8Buffer\[3\]; //根据变量来存放对应的某堆数据的数组 unsigned char i; //for循环用到的变量i const unsigned char \*pCu8; //引入一个指针作为“中间站” void main() //主函数 { switch(Gu8Sec) //根据此选择变量来切换到对应的操作上 { case 1: //第1堆 pCu8=&Cu8Memory\_1\[0\]; //跟第1堆数据“绑定”起来。 break; case 2: //第2堆 pCu8=&Cu8Memory\_2\[0\]; //跟第2堆数据“绑定”起来。 break; case 3: //第3堆 pCu8=&Cu8Memory\_3\[0\]; //跟第3堆数据“绑定”起来。 break; } // for(i=0;i<3;i++) //第1次出现for循环,用来实现“赋值”的“搬运数据”的动作。 // { // Gu8Buffer\[i\]=\*pCu8++; //先把“数据”赋值给数组,然后“指针所存的地址”再自加1。 // } for(i=0;i<3;i++) //第1次出现for循环,用来实现“赋值”的“搬运数据”的动作。 { Gu8Buffer\[i\]=pCu8\[i\]; //这类是“偏移地址”的写法,i在这里相当于指针的偏移地址。 } View(Gu8Buffer\[0\]); //把第1个数Gu8Buffer\[0\]发送到电脑端的串口助手软件上观察。 View(Gu8Buffer\[1\]); //把第2个数Gu8Buffer\[1\]发送到电脑端的串口助手软件上观察。 View(Gu8Buffer\[2\]); //把第3个数Gu8Buffer\[2\]发送到电脑端的串口助手软件上观察。 while(1) { } } /\*---C语言学习区域的结束。-----------------------------------------------\*/ 在电脑串口助手软件上观察到的程序执行现象如下: 开始... 第1个数 十进制:4 十六进制:4 二进制:100 第2个数 十进制:5 十六进制:5 二进制:101 第3个数 十进制:6 十六进制:6 二进制:110 分析: Gu8Buffer\[0\]为4。 Gu8Buffer\[1\]为5。 Gu8Buffer\[2\]为6。 【61.6 如何在单片机上练习本章节C语言程序?】 直接复制前面章节中第十一节的模板程序,练习代码时只需要更改“C语言学习区域”的代码就可以了,其它部分的代码不要动。编译后,把程序下载进带串口的51学习板,通过电脑端的串口助手软件就可以观察到不同的变量数值,详细方法请看第十一节内容。