![单片机C语言程序设计实训100例:基于STC8051+Proteus仿真与实战](https://wfqqreader-1252317822.image.myqcloud.com/cover/270/43738270/b_43738270.jpg)
1.10 数组、字符串与指针
大量单片机应用程序设计会用到数组定义,例如,下面的数组SEG_CODE定义了0~9的七段数码管段码表:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-40-2.jpg?sign=1739309677-mFhdFwsfZ8p0rSqA7uZ2hLCe2hn8Rbjx-0-e9febf617cd6e5141e5f2d7e4ce2a378)
由于程序运行过程中SEG_CODE数组数据保持不变,因此上述语句将存储类型设为code。如果将code改为data也不会影响程序的运行,但程序运行时数组会被分配到RAM中,而不是仅占用Flash ROM空间。在Small模式下,省略code相当于将程序存储类型设为默认的data类型。
编写单片机C语言程序时,如果定义的数组元素是动态变化的,则它必须被定义在RAM中。由于data类型仅允许使用128B内存,如果编译时提示RAM空间不够,可尝试将data改为idata,例如:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-40-3.jpg?sign=1739309677-p1OyjpQ2rYg3H35la6fqdfQfeXrbBlVP-0-914713e72dd97c3fe5cfac715717e4fd)
另外,存储类型code、data和idata还可以放到数据类型前面。
字符串类型在单片机C语言程序设计中也会被大量使用,例如,下面的字节串定义:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-40-4.jpg?sign=1739309677-X12tz7TwxoiVBUJRBKA4azuQ0BQKebpc-0-8804ef6ecb0f3eb57322227bf09988db)
这3种定义是相同的,它们都占用20B存储空间,实际串长均为16个字符,且最后未明确赋值的4个字节全部为0x00(即'\0')。在液晶屏上显示这类字符串时,可用以下方法:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-40-5.jpg?sign=1739309677-6vHnu0mMSfPrAP3eXmy3fdlgFptVBdbl-0-82b3a643ad730b4adcfbb3e2ae716b65)
要注意的是,如果字符串长为16,而字符数组空间也只固定给出了16B,那么上述方法中的后两种就不可靠了。这是因为最后一个字符后面不一定是字符串结束标志'\0'(0x00)。
字符串还可以这样定义:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-40-6.jpg?sign=1739309677-ESmYI6kQQsgP2tdxSy9ZeWGh6cOuB6L6-0-bdeaa4de9ef45d547131a41a451e14fd)
这两种定义也是相同的,其字符串长均为16个字符,所占用存储空间均为17B。这是因为字符串末尾被自动附加了结束标志字节0x00('\0')。
在已知字符串长时,上述3种字符串显示方法均可使用。在字符串长未知时,可使用上述方法中的后两种。另外,上述显示方法还可以改写成:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-41-1.jpg?sign=1739309677-LhTQ89Q2LOUlixIAYVNuxNz0pbMLjI5F-0-a0dc435f1316a9961f7d592f9b9f77da)
在编写C语言程序时,除了常使用字符数组(字符串)以外,还会用到字符串数组,例如:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-41-2.jpg?sign=1739309677-dxR4HlvTLpUCHQAduTTF0dcPk1yK4ULK-0-9efa22cf289d1fd8349846c019319a37)
如果要在液晶屏上显示“Counter:”这个字符串,可用以下语句实现:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-41-3.jpg?sign=1739309677-swT2sSxZDPjKsqoJGAqTiXx08uuuffT7-0-b71e9cf8a6261c189b7f4c5e51eb7554)
在英文字符液晶屏上显示数值时,要将待显示数据转换为字符串。这时,可用此前提到的数据位分解方法,先分解出各位数字,然后加上0x30('0')得到对应数字的ASCII编码。
另一种更为简单的方法是使用sprintf函数,示例代码如下:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-41-4.jpg?sign=1739309677-UA6mjQCY8lGD7IPmajRAvHPddMHUsqTq-0-ecdf033ddb164ddf0eb72711ae32b09f)
上述语句运行后,Buf会被以下字节填充:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-41-5.jpg?sign=1739309677-O1WgPBiwTLeTbentVuvFjrM3tE6SRZGO-0-aa5dc824095758c5b487e4586849a85d)
这些字节代表字符串“-123.45”,其最前面有一空格,用于填充使其总长到8字符,该字符串可直接送液晶屏显示。Keil C跟踪Buf的填充效果如图1-16所示。
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-41-6.jpg?sign=1739309677-u9nYAANxaWL2zv77ewpcUW0ILBG4Tiqi-0-348881472a28358b208e9d8e6711052a)
图1-16 Keil C跟踪Buf的填充效果
如果已经有语句:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-41-7.jpg?sign=1739309677-i99llO57xpokh7zFdxOCW6rr9hapI4iF-0-9b30fca47d0235369f0f011a74ca2d23)
语句sprintf(Buf+7, "%8.2f", x)会使Buf中的字符串变为“Result:-123.45”。此外,可以使用下面的语句得到同样的结果:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-41-8.jpg?sign=1739309677-vtR0gwgtc0d7GoUMUA4EgPUEEeN3dQzY-0-a43da1ffbf04616015bee5d50e9badf3)
另外,C语言还提供了与字符串有关的数据转换函数atoi、atol、atof、strtod、strtol、strtoul。在程序设计中涉及数据输入/输出、运算与显示时,可以恰当使用这些函数。
指针是C语言的重要特色之一,对于语句:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-41-9.jpg?sign=1739309677-AK0wcT3JPzVZXGI45Z8zQ0DIo11iuYfn-0-3cdde9d661543db69e2eafebd8317493)
pd指向数组d中的第0个字节。显示数组内容可使用下面的代码:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-41-10.jpg?sign=1739309677-hCAskFR6gTCConI57L9y57Io3XGz3SoD-0-414eb193caa842092406350ffb0e1101)
但是不能使用下面的代码:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-41-11.jpg?sign=1739309677-BBqd1nTgbwVBi9gw5HbikPv0ivkSY7uo-0-f6353cf46d94884f0816a81fede9f6d4)
数组名d虽然也是第0个字节的地址,但它不能在运行过程中改变,尽管数组名同样是数组中第0个元素的指针。某些函数定义中的形参为数组,调用函数时给出的实参常为指向同类型数据的指针,反之形参为指针,实参为数组名也很常见。
此前讨论的字符串示例中也出现了指针应用,这些应用同样要熟练掌握。
由于8051及其派生系列单片机具有独特的结构,Keil C51支持以下两种不同类型的指针。
1. 通用指针
上述示例u8 *pd中的pd就是通用指针。其指针声明与标准C语言完全一样。其特点是总用3个字节来存储指针,第1个字节表示存储器类型,第2、3个字节分别是指针所指向数据地址的高字节和低字节。这种定义很方便但执行速度较慢,在所指向的目标空间不明确时普遍使用。
2. 存储器指针
存储器指针在定义时指明了存储器类型,并且总指向特定的存储器空间(片内RAM、片外RAM或ROM),例如:
![](https://epubservercos.yuewen.com/DF9E4A/23020648309755406/epubprivate/OEBPS/Images/42553-00-42-1.jpg?sign=1739309677-wx6T9EBQqHC1s9lLNYQZSri8ABzwIcrs-0-e56355ece346b4904111cc3a08226ec2)
由于定义中已经指明了存储器类型,因此相对于通用指针而言,存储器指针第一字节被省略了。对于data、bdata、idata存储器类型,存储器指针仅需要1个字节,因为它们的寻址空间都在256B以内。对code和xdata存储类型,存储器指针则需要2个字节,因为它们的寻址空间最大为64KB。
使用存储器指针比使用通用指针所占存储空间小、执行速度更快。在存储空间一定时,建议使用存储器指针。如果存储空间不确定,则使用通用指针。