
1.10 数组、字符串与指针
大量单片机应用程序设计会用到数组定义,例如,下面的数组SEG_CODE定义了0~9的七段数码管段码表:

由于程序运行过程中SEG_CODE数组数据保持不变,因此上述语句将存储类型设为code。如果将code改为data也不会影响程序的运行,但程序运行时数组会被分配到RAM中,而不是仅占用Flash ROM空间。在Small模式下,省略code相当于将程序存储类型设为默认的data类型。
编写单片机C语言程序时,如果定义的数组元素是动态变化的,则它必须被定义在RAM中。由于data类型仅允许使用128B内存,如果编译时提示RAM空间不够,可尝试将data改为idata,例如:

另外,存储类型code、data和idata还可以放到数据类型前面。
字符串类型在单片机C语言程序设计中也会被大量使用,例如,下面的字节串定义:

这3种定义是相同的,它们都占用20B存储空间,实际串长均为16个字符,且最后未明确赋值的4个字节全部为0x00(即'\0')。在液晶屏上显示这类字符串时,可用以下方法:

要注意的是,如果字符串长为16,而字符数组空间也只固定给出了16B,那么上述方法中的后两种就不可靠了。这是因为最后一个字符后面不一定是字符串结束标志'\0'(0x00)。
字符串还可以这样定义:

这两种定义也是相同的,其字符串长均为16个字符,所占用存储空间均为17B。这是因为字符串末尾被自动附加了结束标志字节0x00('\0')。
在已知字符串长时,上述3种字符串显示方法均可使用。在字符串长未知时,可使用上述方法中的后两种。另外,上述显示方法还可以改写成:

在编写C语言程序时,除了常使用字符数组(字符串)以外,还会用到字符串数组,例如:

如果要在液晶屏上显示“Counter:”这个字符串,可用以下语句实现:

在英文字符液晶屏上显示数值时,要将待显示数据转换为字符串。这时,可用此前提到的数据位分解方法,先分解出各位数字,然后加上0x30('0')得到对应数字的ASCII编码。
另一种更为简单的方法是使用sprintf函数,示例代码如下:

上述语句运行后,Buf会被以下字节填充:

这些字节代表字符串“-123.45”,其最前面有一空格,用于填充使其总长到8字符,该字符串可直接送液晶屏显示。Keil C跟踪Buf的填充效果如图1-16所示。

图1-16 Keil C跟踪Buf的填充效果
如果已经有语句:

语句sprintf(Buf+7, "%8.2f", x)会使Buf中的字符串变为“Result:-123.45”。此外,可以使用下面的语句得到同样的结果:

另外,C语言还提供了与字符串有关的数据转换函数atoi、atol、atof、strtod、strtol、strtoul。在程序设计中涉及数据输入/输出、运算与显示时,可以恰当使用这些函数。
指针是C语言的重要特色之一,对于语句:

pd指向数组d中的第0个字节。显示数组内容可使用下面的代码:

但是不能使用下面的代码:

数组名d虽然也是第0个字节的地址,但它不能在运行过程中改变,尽管数组名同样是数组中第0个元素的指针。某些函数定义中的形参为数组,调用函数时给出的实参常为指向同类型数据的指针,反之形参为指针,实参为数组名也很常见。
此前讨论的字符串示例中也出现了指针应用,这些应用同样要熟练掌握。
由于8051及其派生系列单片机具有独特的结构,Keil C51支持以下两种不同类型的指针。
1. 通用指针
上述示例u8 *pd中的pd就是通用指针。其指针声明与标准C语言完全一样。其特点是总用3个字节来存储指针,第1个字节表示存储器类型,第2、3个字节分别是指针所指向数据地址的高字节和低字节。这种定义很方便但执行速度较慢,在所指向的目标空间不明确时普遍使用。
2. 存储器指针
存储器指针在定义时指明了存储器类型,并且总指向特定的存储器空间(片内RAM、片外RAM或ROM),例如:

由于定义中已经指明了存储器类型,因此相对于通用指针而言,存储器指针第一字节被省略了。对于data、bdata、idata存储器类型,存储器指针仅需要1个字节,因为它们的寻址空间都在256B以内。对code和xdata存储类型,存储器指针则需要2个字节,因为它们的寻址空间最大为64KB。
使用存储器指针比使用通用指针所占存储空间小、执行速度更快。在存储空间一定时,建议使用存储器指针。如果存储空间不确定,则使用通用指针。