打卡第十一集-单片机C语言程序设计导入一
C51对ANSI C的扩展

C51(Keil C51编译器)支持的数据类型可以分为标准C数据类型和针对8051单片机的扩展数据类型两大类。
下表汇总了C51支持的所有数据类型及其关键属性:
| 数据类型 |
位数 (Bits) |
字节数 (Bytes) |
取值范围 |
说明 |
| 基本数据类型 |
|
|
|
|
bit |
1 |
— |
0 或 1 |
位类型,用于位寻址 |
signed char |
8 |
1 |
-128~ +127 |
有符号字符型 |
unsigned char |
8 |
1 |
0~ 255 |
无符号字符型,常用于存储8位数据 |
signed int |
16 |
2 |
-32768~ +32767 |
有符号整型 |
unsigned int |
16 |
2 |
0~ 65535 |
无符号整型 |
signed long |
32 |
4 |
-2147483648~ +2147483647 |
有符号长整型 |
unsigned long |
32 |
4 |
0~ 4294967295 |
无符号长整型 |
float |
32 |
4 |
±1.175494E-38~ ±3.402823E+38 |
单精度浮点型,符合IEEE-754标准 |
enum |
8 / 16 |
1 或 2 |
-128~127 或 -32768~32767 |
枚举型 |
* (指针) |
8~24 |
1~3 |
取决于存储类型 |
指针变量,用于存放地址 |
| 扩展数据类型 |
|
|
|
|
sfr |
8 |
1 |
0~ 255 |
用于定义8位特殊功能寄存器 |
sfr16 |
16 |
2 |
0~ 65535 |
用于定义16位特殊功能寄存器 |
sbit |
1 |
— |
0 或 1 |
用于定义特殊功能寄存器中的可寻址位 |
💡 数据类型详解
1. 标准C数据类型
这些类型与标准C语言类似,但在8051这种8位单片机架构下有特定的大小和实现:
char类型:占据1个字节,是C51中最基础的数据类型。常用 unsigned char来存储0-255的数值或ASCII字符,节省内存且处理效率高。
int类型:占据2个字节,用于需要更大范围的整数运算。当 char和 int混合运算时,char会自动转换为 int类型。
long类型:占据4个字节,用于处理大范围的整数。
float类型:占据4个字节,用于带小数点的浮点数运算。由于处理浮点数会占用较多代码空间和CPU时间,在资源受限的单片机中应谨慎使用。
enum类型:枚举类型,根据数值范围自动选择占用1或2个字节。
2. 针对8051的扩展数据类型
这部分是C51为了直接高效地操作8051单片机硬件而特有的数据类型:
-
bit:位类型,用于定义一个位变量。它类似于布尔类型,只能取0或1。bit变量会被存放在8051内部RAM的位寻址区(20H~2FH),非常节省资源。
-
sfr (Special Function Register):特殊功能寄存器类型。8051单片机的许多功能控制(如I/O端口、定时器)都是通过读写SFR实现的。使用 sfr可以为特定的SFR地址命名,方便操作。例如:
c
sfr P1 = 0x90; // 定义P1端口,地址为0x90
P1 = 0xFF; // 将P1口所有引脚设为高电平
-
sfr16:16位特殊功能寄存器类型。用于一次性操作16位的SFR,例如定时器T0的16位计数初值(由TH0和TL0两个8位寄存器组成)。
-
sbit (Special Bit):特殊功能寄存器中的位类型。用于直接访问SFR或可位寻址RAM中的某一位,常用于引脚状态的读取和控制。例如:
c
sbit LED = P1^0; // 定义LED为P1.0引脚
LED = 0; // 将P1.0引脚拉低,点亮LED
🔄 数据类型转换
在C51中,不同类型的数据进行运算时,编译器会遵循一定的规则进行自动类型转换,优先级大致为:bit → char → int → long → float → signed → unsigned。你也可以使用强制类型转换符 () 来人为地将某个表达式转换为所需的类型。
C51通过这些丰富且针对性的数据类型,既保留了C语言的高级特性,又让开发者能精确地控制8051单片机的每一个硬件资源。
在C51(Keil C51编译器)中,指针的使用比标准C更加复杂,这是因为8051单片机具有多种物理存储空间(内部RAM、外部RAM、代码ROM等),每种存储空间的地址宽度和访问方式不同。C51通过引入存储类型限定符和指针类型来精确控制指针的指向和存储位置,以达到高效利用资源的目的。
一、指针的基本声明语法
C51指针的声明格式为:
text
[存储类型] 数据类型 [存储器类型 *] 指针变量名;
- 存储类型:指针变量本身存放在哪个存储区(
data、xdata、pdata等)。
- 数据类型:指针所指向的数据的类型。
- 存储器类型:指针指向的对象位于哪个存储区(如
xdata、code等)。
二、指针的两大分类
1. 通用指针(Generic Pointer)
通用指针可以指向任何存储空间的对象,声明时不指定目标存储类型。编译器会为通用指针分配3个字节(第一字节表示存储类型,后两字节为16位地址)。
c
char *ptr; // 通用指针,占3字节,可指向任何存储区的char
int *iptr; // 通用指针,占3字节
优点:灵活,可以指向不同存储区的变量。
缺点:占用内存多,访问速度慢(因为每次需要解析存储类型)。
2. 基于存储器的指针(Memory-Specific Pointer)
基于存储器的指针在声明时指定了目标存储区,因此指针本身只存放地址(1或2字节),不包含存储类型信息。
| 目标存储区 |
地址宽度 |
指针大小 |
声明示例 |
data, idata, bdata |
8位 |
1字节 |
char data *p; |
pdata |
8位 |
1字节 |
int pdata *p; |
xdata |
16位 |
2字节 |
long xdata *p; |
code |
16位 |
2字节 |
void code *p; |
优点:占用内存小,访问效率高。
缺点:只能指向指定存储区的对象。
三、存储类型对指针本身位置的影响
指针变量本身也可以存放在不同存储区,通过声明时在变量名前添加存储类型来实现。
c
char *data p1; // p1存放在data区,指向char型数据(通用指针,占3字节)
char xdata *p2; // p2存放在xdata区,指向char型数据(通用指针,占3字节)
char *xdata p3; // p3存放在xdata区,指向char型数据(通用指针,占3字节)——注意与上行的区别
char xdata *xdata p4; // p4存放在xdata区,指向xdata区的char(基于存储器的指针,占2字节)
关键点:* 左边的存储类型表示指针指向的目标的存储区;变量名左边的存储类型表示指针变量自身的存储区。
四、常用存储区说明
| 存储类型 |
物理空间 |
地址宽度 |
访问速度 |
说明 |
data |
内部RAM低128字节 |
8位 |
最快 |
直接寻址,可位寻址 |
idata |
内部RAM全部256字节 |
8位 |
快 |
间接寻址 |
bdata |
内部RAM位寻址区(20H~2FH) |
8位 |
快 |
可位寻址 |
pdata |
外部RAM一页(256字节) |
8位 |
较慢 |
分页访问,使用MOVX @Ri |
xdata |
外部RAM全部64KB |
16位 |
慢 |
使用MOVX @DPTR |
code |
程序存储器(ROM) |
16位 |
只读 |
存放常量 |
五、指针的初始化与使用
c
#include <reg51.h>
// 定义不同存储区的变量
unsigned char data var1 = 0x12; // data区变量
unsigned char xdata var2 = 0x34; // xdata区变量
unsigned char code var3[] = {0x56, 0x78}; // code区常量数组
void main(void) {
// 通用指针,可指向任意存储区
unsigned char *ptr_g;
ptr_g = &var1; // 指向data区
ptr_g = &var2; // 指向xdata区
ptr_g = var3; // 指向code区
// 基于存储器的指针,只能指向指定存储区
unsigned char data *ptr_d; // 指向data区的指针
ptr_d = &var1; // 合法
// ptr_d = &var2; // 错误!不能指向xdata区
unsigned char xdata *ptr_x; // 指向xdata区的指针
ptr_x = &var2; // 合法
unsigned char code *ptr_c; // 指向code区的指针
ptr_c = var3; // 合法
}
六、指针运算与数组
C51支持标准C的指针运算(++、--、加减整数等),但要注意:
- 通用指针运算时,编译器根据指针当前的存储类型决定步长,但效率较低。
- 基于存储器的指针运算效率高,因为步长固定。
示例:遍历xdata数组
c
unsigned int xdata array[10] = {0};
unsigned int xdata *p;
for (p = array; p < array + 10; p++) {
*p = 0xFFFF;
}
七、特殊注意事项
1. 不能指向SFR
特殊功能寄存器(SFR)虽然地址在0x80~0xFF,但SFR不是RAM,不能用普通指针指向。操作SFR应使用 sfr和 sbit关键字。
2. 空指针
C51中通用指针的NULL是三个字节全零,基于存储器的指针NULL是地址部分为零。但不同存储区的零地址可能有效(如data区的0地址),因此使用指针前最好检查是否合法。
3. 类型强制转换
可以将一种指针类型强制转换为另一种,但需注意存储区的兼容性。例如,将 xdata指针转换为 data指针可能导致错误,因为地址宽度可能不匹配。
4. 代码常量的访问
指向 code区的指针只能读取,不能写入。
八、指针选择建议
- 追求速度、内存紧张:使用基于存储器的指针,并合理分配存储区。
- 需要灵活性:使用通用指针,但要注意其3字节的开销和较慢的访问速度。
- 指向大数组或外部RAM:使用
xdata *指针。
- 指向常量字符串或查表:使用
code *指针。
九、总结
C51的指针系统是对标准C的扩展,旨在适应8051的分区内存架构。理解存储类型和指针分类,有助于写出高效、紧凑的嵌入式代码。合理使用基于存储器的指针可以显著减少RAM占用并提升执行速度。