admin 管理员组文章数量: 887021
2024年1月28日发(作者:二叉树遍历 c语言)
变量是在程序运行过程中其值可以改变的量。在C51中,在使用变量前必须对变量进行定义,指出变量的数据类型和存储模式,以便编译系统为它分配相应的存储单元。变量的定义格式如下:
[存储种类] 数据类型说明符 [存储器类型] 变量名1[=初值],变量名2[=初值]„;
(1)格式说明
1)存储种类是指变量在程序执行过程中的作用范围。C51变量的存储种类有四种,分别是自动(auto)、外部(extern)、静态(static)和寄存器(register)。定义变量时,如果省略存储种类,则该变量默认为自动(auto)变量。
用auto定义的变量作用范围仅在定义它的函数体或复合语句内部有效。
用extern定义的变量称为外部变量,其作用范围为整个程序。
用static定义的变量称为静态变量。其作用范围仅在定义的函数体内有效,一直存在,再次进入该函数时,变量的值为上次结束函数时的值。
用register定义的变量称为寄存器变量,处理速度快,但数目少。C51编译器编译时能自动识别程序中使用频率最高的变量,并自动将其作为寄存器变量,用户无需专门声明。
2)在定义变量时,必须通过数据类型说明符指明变量的数据类型,指明变量在存储器中占用的字节数。可以是基本数据类型说明符,也可以是组合数据类型说明符,还可以是用typedef和#define定义的类型别名。别名要按用户自定义标识符的原则命名。例如:使用“#define uchar unsigned char”定义了“uchar”,则可以使用这个类型定义变量。
3)存储器类型是用于指明变量所处的单片机的存储器区域情况。省略则默认为data类型,即片内前128字节的RAM;bdata为可位寻址内部数据存储器,定义的变量可以用sbit定义位变量访问其中的二进制位;idata可以访问51的内部256字节的RAM;code定义的变量存储在程序存储器,只能读出不能写入,相当于常量。
4)变量名是C51区分不同变量,为不同变量取的名称,也就是用户自定义标识符,要遵循标识符的命名原则。
5)允许在一个类型说明符后,定义多个相同类型的变量。各变量名之间用逗号隔开,类型说明符与变量名之间至少用一个空格间隔。
6)最后一个变量名之后必须以“;”号结尾。
7)变量定义必须放在变量使用之前。一般放在函数体的开头部分。
(2)变量定义示例
int a,b,c=2; //a,b,c为整型变量,并将变量c的初值赋为2
long x,y; //x,y为长整型变量
unsigned char p,q; //p,q为无符号字符型变量
float t=-2.3; //定义实型变量t,并给t赋初值为-2.3
code float Vref=2.5;//定义变量Vref为实型,初始值为2.5,只读
一、变量的作用范围
从变量的作用范围来看,有全局变量和局部变量之分。
全局变量是指在程序开始处或各个功能函数的外面定义的变量,在程序开始处定义的全局变量对于整个程序都有效,可供程序中所有函数共同使用;而在各功能函数外面定义的全局变量只对从定义处开始往后的各个函数有效,只有从定义处往后的那些功能函数才可以使用该变量,定义处之前的函数则不能使用它。
局部变量是指在函数内部或以花括号{ }围起来的功能块内部所定义的变量,局部变量只在定义它的函数或功能块以内有效,在该函数或功能块以外则不能使用它。局部变量可以与全局变量同名,但在这种情况下局部变量的优先级较高,而同名的全局变量在该功能块内被暂时屏蔽。
二、变量的生存期
变量的生存期即该变量存在的时间。从变量的存在时间来看又可分为静态存储变量和动态存储变量。静态存储变量是指该变量在程序运行期间其存储空间固定不变;动态存储变量是指该变量的存储空间不确定,在程序运行期间根据需要动态地为该变量分配存储空间。
在定义变量时,可以指定变量的存储种类。在C51中,变量的存储种类有四种:自动(auto)、外部(extern)、静态(static)和寄存器(register)。
1、自动变量
用关键字auto作存储类型说明的局部变量(包括形参)称为自动变量。
1
自动变量的默认范围在定义它的函数体或符合语句内部,只有在定义它的函数被调用,或是定义它的复合语句被执行时,编译器才为其分配内存空间,开始其生存期。当函数调用结束返回,或复合语句执行结束时,自动变量所占用的内存空间就被释放,其生存期结束,占用的内存空间有可能分配给其他函数中定义的自动变量。
当函数被再次调用或符合语句被再次执行时,自动变量所对应的内存空间的值将不确定,有可能不是上次运行时的值,因而必须被重新赋值。
2、外部变量(extern)
按照默认规则,凡是在所有函数之前,在函数外部定义的变量都是外部变量,定义时可以不写extern说明符。但是,在一个函数体内说明一个已在该函数体外或别的程序模块文件中定义过的外部变量时,则必须使用extern说明符。一个外部变量被定义后,它就被分配了固定的内存空间。
外部变量的生存期为程序的整个执行时间,即在程序执行期间外部变量可以被随意使用,当一条复合语句执行完毕或是某一函数返回时,外部变量的存储空间并不被释放,其值也仍然保留。一次外部变量属于全局变量。
C51允许将大型程序分解为若干个独立的程序模块文件,各个模块可分别进行编译,然后再将它们连接在一起。在这种情况下,如果某个变量需要在所有程序模块文件中使用,只要在一个程序模块文件中将该变量定义成全局变量,而在其他程序模块文件中用extern说明该变量是已经被定义过的外部变量就可以了。
3、静态变量(static)
静态变量不像自动变量那样只有当函数调用它时才存在,退出函数时它就消失,静态变量所分配的内存空间是独占的,始终都是存在的。静态变量只能在定义它的函数内部进行访问,退出函数时,变量的值仍然保持但是不能进行访问。使用静态变量需要占用较多的内存空间,而且降低了程序的可读性。
4、寄存器变量(register)
编译器给使用register定义的变量分配单片机的通用寄存器空间,有较快的运行速度。寄存器变量可以被认为是自动变量的一种,它的有效作用范围也与自动变量相同。
由于单片机中的寄存器是有限的,不能所有变量都定义成寄存器变量。CX51编译器能够识别程序中使用频率最高的变量,在可能的情况下,即使程序中并未将该变量定义为寄存器变量,编译器也会自动将其作为寄存器变量处理。
一般来说全局变量为静态存储变量,局部变量为动态存储变量。
表A2-1中列出了 KEIL uVision2 C51 编译器所支持的数据类型。在标准 C 语 言中基本的数据类型为 char,int,short,long,float 和 double,而在 C51 编译器中 int 和 short 相同,float 和 double 相同,这里就不列出说明了。
附表1 KEIL C51 编译器所支持的数据类型
数据类型 长度 值域
unsigned char 单字节 0~255
signed char 单字节 -128~+127
unsigned int 双字节 0~65535
signed int 双字节 -32768~+32767
unsigned long 四字节 0~4294967295
signed long 四字节 -2147483648~+2147483647
float 四字节 ±1.175494E-38~±3.402823E+38
* 1~3字节 对象的地址
bit 位 0或1
sfr 单字节 0~255
sfr16 双字节 0~65535
sbit 位 0或1
具体的类型说明如下:
1.char 字符类型
char 类型的长度是一个字节,通常用于定义处理字符数据的变量或常量。分无符号字 符类型 unsigned char
2
和有符号字符类型 signed char,默认值为 signed char 类型。
unsigned char 类型用字节中所有的位来表示数值,所可以表达的数值范围是 0~255。 signed char 类型用字节中最高位字节表示数据的符号,“0”表示正数,“1”表示负数, 负数用补码表示。所能表示的数值范围是-128~+127。unsigned char 常用于处理 ASCII 字符或用于处理小于或等于 255 的整型数。
正数的补码与原码相同,负二进制数的补码等于它的绝对值按位取反后加 1。
2. int 整型
int 整型长度为两个字节,用于存放一个双字节数据。分有符号 int 整型数 signed int 和无符号整型数
unsigned int,默认值为 signed int 类型。signed int 表示的数值范 围是-32768~+32767,字节中最高位表示数据的符号,“0”表示正数,“1”表示负数。 unsigned int 表示的数值范围是 0~65535。
3. long 长整型
long 长整型长度为四个字节,用于存放一个四字节数据。分有符号 long 长整型 signed long 和无符号长整型 unsigned long,默认值为 signed long 类型。signed int 表示的数值范围是-2147483648~+2147483647,字节中最高位表示数据的符号,“0”表示正数,“1”表示负数。unsigned long 表示的数值范围是 0~4294967295。
4. float 浮点型
float 浮点型在十进制中具有 7 位有效数字,是符合 IEEE-754 标准的单精度浮点型数据,占用四个字节。因浮点数的结构较复杂在以后的章节中再做详细的讨论。
5. * 指针型
指针型本身就是一个变量,在这个变量中存放的指向另一个数据的地址。这个指针变量要占据一定的内存单元,对不同的处理器长度也不尽相同,在 C51 中它的长度一般为 1~3 个字节。指针变量也具有类型,在以后的课程中有专门一课做探讨,这里就不多说了。
6.bit 位标量
bit 位标量是 C51 编译器的一种扩充数据类型,利用它可定义一个位标量,但不能定义位指针,也不能定义位数组。它的值是一个二进制位,不是 0 就是 1,类似一些高级语 言中的 Boolean 类型中的 True 和 False。
7.sfr 特殊功能寄存器
sfr 也是一种扩充数据类型,占用一个内存单元,值域为 0~255。利用它可以访问 51 单片机内部的所有特殊功能寄存器。如用 sfr P1 = 0x90 这一句定义P1为P1端口在片内的寄存器,在后面的语句中我们可以用 P1
= 255(对 P1 端口的所有引脚置高电平)之类的语句来操作特殊功能寄存器。
8.sfr16 16 位特殊功能寄存器
sfr16 占用两个内存单元,值域为 0~65535。sfr16 和 sfr 一样用于操作特殊功能寄存 器,所不同的是它用于操作占两个字节的寄存器,如定时器 T0 和 T1。
9. sbit 可寻址位
sbit 寻址位是 C51 中的一种扩充数据类型,利用它可以访问芯片内部的 RAM 中的可寻址位或特殊功能寄存器中的可寻址位。如我们定义了:
sfr P1 = 0x90; //因 P1 端口的寄存器是可位寻址的
因此,我们可以定义:
sbit P1_1 = P1^1; //P1_1 为 P1 中的 P1.1 引脚
三、常量、变量、关键字和标识符
1.关键字和标识符
在C语言中的标识符和关键字在C51中一般都有。
标识符是用来标识源程序中某个对象的名字,这些对象可以是语句、数据类型、函数、变量、数组等等。标识符由字符、数字和下划线等组成,是由字符或下划线开头的字符串。
C语言是大小字敏感的一种高级语言,如“timer1”和“TIMER1”,是两个完全不同定义的标识符。有些编译系统专用的标识符是以下划线开头,所以自定义标识符一般不要以下划线开头命名。
用户根据需要定义标识符称为用户标识符,在命名这些标识符时应当简单,含义清晰,做到“见名知义”,这样有助于阅读理解程序。在 C51 编译器中,只支持标识符的前32 位为有效标识。
关键字则是C语言规定了一批标识符,它们具有固定名称和含义,不能另作它用,在程序编写中不允许标识符与关键字相同。例如int、float等有专门的用途,它们不能用来再作变量名或函数名之类标识符,请参3
阅C语言书籍。
2.常量
常量是在程序运行过程中不能改变值的量,而常量的数据类型只有整型、浮点型、字符型、字符串型和位标量。
常量的数据类型说明如下所示:
⑴.整型常量可以表示为十进制如 123,0,-89 等。十六进制则以 0x 开头如 0x34,-0x3B等。长整型就在数字后面加字母 L,如 104L,034L 等。
⑵.浮点型常量可分为十进制和指数表示形式。十进制由数字和小数点组成,如0.888,3345.345,0.0 等,整数或小数部分为 0,可以省略但必须有小数点。指数表示形式为[±]数字[.数字]e[±]数字,[]中的内容为可选项,其中内容根据具体情况可有可无,但其余部分必须有,如 125e3,7e9,-3.0e-3。
⑶.字符型常量是单引号内的字符,如‘a’,‘d’等,不可以显示的控制字符,可以在该字符前面加一个反斜杠“”组成专用转义字符。
⑷.字符串型常量由双引号内的字符组成,如“test”,“OK”等。当引号内的没有字符时,为空字符串。在使用特殊字符时同样要使用转义字符如双引号。在 C语言中字符串常量是作为字符类型数组来处理的,在存储字符串时系统会在字符串尾部加上o 转义字符以作为该字符串的结束符。字符串常量“A”和字符常量‘A’是不同的,前者在存储时多占用一个字节的空间。
⑸.位常量,它的值是一个二进制。
常量可用在不必改变值的场合,如固定的数据表,字库等。常量的定义方式有多种,下面举例来加以说明。
#difine False 0x0; //用预定义语句可以定义常量
#difine True 0x1; //这里定义 False 为 0,True 为 1
在程序中用到 False 编译时自动用 0 替换,同理 True 替换为 1。
3.变量
变量就是一种在程序执行过程中其值能不断变化的量。要在程序中使用变量必须先用标识符作为变量名,并指出所用的数据类型和存储模式,这样编译系统才能为变量分配相应的存储空间。变量的定义可以使用所有 C51 编译器支持的数据类型,定义一个变量的格式如下:
[存储种类] 数据类型 [存储器类型] 变量名表
在定义格式中除了数据类型和变量名表是必要的,其它都是可选项。
存储种类有四种: 自动(auto),外部(extern),静态(static)和寄存器(register),默认类型为自动(auto)。存储种类改变变量用什么形式存储,可以交给 KEIL进行优化处理,编程时一般采用默认。
数据类型是表示在单片机程序中占用存储空间和数据形式的定义,可以是整型、浮点型、字符型、字符串型和位变量等C51支持的各种类型。
存储器类型的说明就是指定该变量在 C51 硬件系统中所使用的存储区域,并在编译时准确的定位。表
A3-1 中是 KEIL uVision2 所能认别的存储器类型。注意的是在 AT89S51 芯片中 RAM 只有低 128 位,位于 80H 到 FFH 的高 128 位则在 52 芯片中才有用,并和特殊寄存器地址重叠。
附表2 存储器类型
存储器类型 说明
data 直接访问内部数据存储器(128 字节),访问速度最快
bdata 可位寻址内部数据存储器(16 字节),允许位与字节混合访问
idata 间接访问内部数据存储器(256 字节),允许访问全部内部地址
pdata 分页访问外部数据存储器(256 字节),用 MOVX @Ri 指令访问
xdata 外部数据存储器(64KB),用 MOVX @DPTR 指令访问
code 程序存储器(64KB),用 MOVC @A+DPTR 指令访问
如果省略存储器类型,系统则会按编译模式SMALL,COMPACT或LARGE所规定的默认存储器类型去指定变量的存储区域。无论什么存储模式都可以声明变量在任何的8051存储区范围,然而把最常用的命令如循环计数器和队列索引放在内部数据区可以显著的提高系统性能。还有要指出的就是变量的存储种类与存储器类型是完全无关的。
4
SMALL存储模式把所有函数变量和局部数据段放在8051系统的内部数据存储区这使访问数据非常快,但SMALL存储模式的地址空间受限。在写小型的应用程序时,变量和数据放在data内部数据存储器中是很好的因为访问速度快,但在较大的应用程序中data区最好只存放小的变量、数据或常用的变量(如循环计数、数据索引),而大的数据则放置在别的存储区域。
COMPACT存储模式中所有的函数和程序变量和局部数据段定位在8051系统的外部数据存储区。外部数据存储区可有最多256字节(一页),在本模式中外部数据存储区的短地址用@R0/R1。
LARGE存储模式所有函数和过程的变量和局部数据段都定位在8051系统的外部数据区外部数据区最多可有64KB,这要求用DPTR数据指针访问数据。
作为变量中的sfr,sfr16,sbit定义变量的方法。
sfr和sfr16可以直接对51单片机的特殊寄存器进行定义,定义方法如下:
sfr 特殊功能寄存器名= 特殊功能寄存器地址常数;
sfr16 特殊功能寄存器名= 特殊功能寄存器地址常数;
如可以这样定义AT89S51的P1口
sfr P1 = 0x90; //定义P1 I/O口,其地址90H
sfr 关键字后面是一个要定义的名字,可任意选取,但要符合标识符的命名规则,名字最好有一定的含义如P1口可以用P1为名,这样程序会变的好读好多。等号后面必须是常数,不允许有带运算符的表达式,而且该常数必须在特殊功能寄存器的地址范围之内(80H-FFH),具体可查看附录中的相关表。sfr是定义8位的特殊功能寄存器而sfr16则是用来定义16位特殊功能寄存器,如8052的T2定时器,可以定义为:
sfr16 T2 = 0xCC; //这里定义8052定时器2,地址为T2L=CCH,T2H=CDH
用 sfr16 定义 16 位特殊功能寄存器时,等号后面是它的低位地址,高位地址一定要位于物理低位地址之上。注意的是不能用于定时器 0 和 1 的定义。
sbit 可定义可位寻址对象。如访问特殊功能寄存器中的某位。其实这样应用是经常要用的如要访问 P1
口中的第 2 个引脚 P1.1,可以用以下的方法去定义:
(1) sbit 位变量名=位地址
sbit P1_1 = Ox91;
这样是把位的绝对地址赋给位变量。同sfr一样sbit 的位地址必须位于80H-FFH之间。
(2) Sbit 位变量名=特殊功能寄存器名^位位置
sfr P1 = 0x90;
sbit P1_1 = P1^1; //先定义一个特殊功能寄存器名再指定位变量名所在的位置当可寻址位位于特殊功能寄存器中时可采用这种方法
(3)sbit 位变量名=字节地址^位位置
sbit P1_1 = 0x90^1;
这种方法其实和(2)是一样的,只是把特殊功能寄存器的位址直接用常数表示。
在 C51 存储器类型中提供有一个 bdata 的存储器类型,这个是指可位寻址的数据存储器,位于单片机的可位寻址区中,可以将要求可位录址的数据定义为 bdata,如:
unsigned char bdata ib; //在可位录址区定义 ucsigned char 类型的变量 ib
int bdata ab[2]; //在可位寻址区定义数组 ab[2],这些也称为可寻址位对象
sbit ib7=ib^7 //用关键字 sbit 定义位变量来独立访问可寻址位对象的其中一位
sbit ab12=ab[1]^12;
操作符“^”后面位的位置最大值取决于指定的基址类型,char 0-7,int 0-15,long 0-31。
指针是C51中的一个十分重要概念,C51中专门规定了一种指针类型的数据。变量的指针就是该变量的地址,还可以定义一个指向某个变量的指针变量。为了表示指针变量和它所指向的变量地址之间的关系,C51提供了两个专门的运算符:*(取内容)和&(取地址)。
取内容和取地址运算的一般形式分别为:
变量 =* 指针变量
指针变量 =& 目标变量
5
取内容运算的含义是将指针变量所指向的目标变量的值赋给左侧的变量;取地址运算的含义是将目标变量的地址赋给左侧的变量。需要注意的是,指针变量中只能存放地址(即指针型数据),不要将一个非指针类型的数据赋值给一个指针变量。例如,下面的语句完成对指针变量赋值(地址值):
char data *p /*定义指针变量*/
p =30H /*给指针变量赋值,30H为8051片内RAM地址*/
11. C51对存储器和特殊功能寄存器的访问
虽然可以采用指针变量对存储器地址进行操作,但是由于8051单片机存储器结构自身的特点,C51提供了另外一种访问方法,即利用库函数中的绝对地址访问头文件“ absacc.h”来访问不同区域的存储器,以及片外扩展I/O端口。在“ absacc.h”头文件中进行了如下宏定义:
CBYTE[地址](访问CODE区char型)
DBYTE[地址](访问DATA区char型)
PBYTE[地址](访问PDATA区或I/O端口 char型)
XBYTE[地址](访问XDATA区或I/O端口 char型)
CWORD[地址](访问CODE区int型)
DWORD[地址](访问DATA区int型)
PWORD[地址](访问PDATA区或I/O端口 int型)
XWORD[地址](访问XDATA区或I/O端口 int型)
下面语句完成向片外扩展端口地址7FFFH写入一个字符型数据:
XBYTE[OX7FFF]= 0x80;
下面语句将int型数据 0x9988送入外部 RAM单元见0000H和 000lH:
XWORD[0]= 0X9988;
如果采用如下语句定义一个D/A转换器端口地址:
#define DAC0832 XBYTE[ox7FFF]
那么,程序文件中所有出现DAC0832的地方,就是对地址为OX7FFFH的外部RAM单元或I/O端口进行访问。
8051单片机具有100多个品种,为了方便访问不同品种单片机内部特殊功能寄存台,C51提供了多个相关头文件,如 reg5l.h、reg52.h等。在头文件中对单片机内部特殊功能寄存器及其有位名称的可寻址位进行了定义,编程时只要根据所采用的单片机,主程序文件开始处用文件包含处理命令“#include”将相关头文件包含进来,然后就可以直接引用特殊功能寄存器(注意必须采用大写字母)了。例如,下面语句完成的8051定时方式寄存器TMOD的赋值:
#include
TMOD=0X2 0;
12.强制类型转换运算符
C语言中的圆括号“()”也可以作为一种运算符使用,这就是强制类型转换运算符,它的作用是将表达式或变量的类型强制转换成为所指定的类型。在C51程序中进行算术运算时,需要注意数据类型的转换,数据类型转换分为隐式转换和显式转换。隐式转是在对程序进行编译时由编译器自动处理的,并且只有基本数据类型(即char、nit、long和 float)可以进行隐式转换。其他数据类型不能进行隐式转换,例如,不能把一个整型数利用隐式转换赋值给一个指针变量,在这种情况下就必须利用强制类型转奖运算符来进行显式转换。强制类型转换运算符的一般使用形式为:
(类型)(表达式)
显示强制类型转换在给指针变量赋值时特别有用。例如,预先在8051单片机的片外数据存储器(xdata)中定义了一个字符型指针变量px,如果想给这个指针变量赋一初值0xB000,可以写成:px=(char xdata*)oxB000。这种方法特别适合于用标识符来存取绝对地址。
6
版权声明:本文标题:C51变量说明 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/free/1706412393h506126.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论