正如你可能已经猜到的,指针和数组之间有着密切的关系。清单8-3展现了显示字符数组内容的简单程序。
代码清单8-3.Display Character Array
当你运行这个程序时,输出只是“Hello”。为此,你使用数组索引遍历字符串。现在将for循环中的语句更改为:
Serial.print(*(greet+i));编译、上传和运行程序。输出会发生什么变化?绝对没有。程序仍然显示“Hello”。现在尝试将同一语句更改为:
Serial.print(*(greet+i*sizeof(char)));输出是否发生变化?不,还是一样。原因是这两种变体都利用了一个事实,即单独使用数组名与使用数组的左值相同。
图8-9 字符数组
假设greet数组从内存地址2200开始存储(即,其左值为2200)。现在看看这句话:
Serial.print(*(greet+i));
在第一次通过循环时,由于i为0,语句解析为:
Serial.print(*(greet+0));
Serial.print(*(2200+0));
Serial.print(*(2200));
间接寻址操作符只是说转到内存地址2200,并获取在那里找到的字符。这是字母“H”。在下一次通过for循环时,语句解析为:
Serial.print(*(greet+1));
Serial.print(*(2200+1));
Serial.print(*(2201));
间接寻址操作符获取“e”。该过程重复,直到循环结束,此时屏幕上显示“Hello”一词。该声明的第二个变体是:
Serial.print(*(greet+i*sizeof(char)));
sizeof()运算符返回存储由括号括起的数据类型所需的字节数。从表3-1中可以看出,字符需要1个字节才能存储在内存中。因此,本声明决定:
Serial.print(*(greet+i*1));
Serial.print(*(greet+0*1));
Serial.print(*(2200+0));(www.xing528.com)
Serial.print(*(2200));
并显示“H”。对于第二遍,语句解析为:
Serial.print(*(greet+i*1));
Serial.print(*(greet+1*1));
Serial.print(*(2200+1));
Serial.print(*(2201));
并显示“e”。你应该能够理解其余部分的字符。
这个练习应该让你确信使用数组名greet与greet[]数组的左值相同。现在,将以下指针定义添加到loop()并更改for语句,如以下代码所示:
void loop(){
char greet[6];
char*ptr;
同样,程序的执行过程与之前完全相同。声明:
ptr=greet;
获取greet的左值并将其放入ptr的右值中。对于编译器来说,数组的名称与数组的左值相同,因此不需要使用运算符的地址(事实上,如果你确实尝试使用运算符的地址,那么编译器会发出一条错误消息)。该语句基本上执行与图8-4所示完全相同的操作。它初始化ptr以指向greet[]数组。在for循环中,语句:
Serial.print(*ptr++);
使用间接运算符(*)获取并显示ptr的内容。因为ptr等于2200,所以显示字母“H”。你在ptr上使用了后增量运算符,所以在下一次通过循环时,将对内存地址2201执行间接寻址,并显示“e”。如你所见,程序的所有三种变体都会产生相同的结果。
还可以使用以下语句对指针进行后减量:
Serial.print(*ptr--);
但是,指针会在每次递减操作中按标量字节递减“备份”指针。除非你能正确地跟踪这一点,否则你可能会后退太远,所操作的数据将是错误的。
如果不使用for循环,只使用以下语句,会发生什么情况?
Serial.print(greet);
同样,该程序的工作原理与之前完全相同。原因是我们现在正在治疗字符串数据类型的字符数组。通过将字符序列为null终止字符('\0')与初始化字符串时一样,可以将字符数组视为一个串。也就是说,Serial.print()函数获取greet[]数组的左值,但可以将其作为一个字符串,因为它的终止字符为空。如果忘记将空字符添加到greet[],没问题,函数的作用是在内存显示过程中不断旋转在读取一个值为0的字节之前找到的任何垃圾。注释掉最后一个初始化字节,然后试试看。笔者的程序在停止打印之前只显示了大约3字节的垃圾,你的结果可能会与众不同。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。