单片机C语言入门讲座
从延时程序谈起——数据类型的知识

🏠 首页 《无线电》杂志 2004年 🔗 第4期 🔗 第39页 分类:电脑与单片机 🔗 周坚 🔗

通过前面几期的学习,我们已经掌握了3种常用的程序结构,学会编写一些简单的单片机C语言应用程序,本期将学习C语言中数据类型的有关知识。

前面的课程中多次用到了延时程序:

void mDelay(unsigned int De-layTime)

?邀unsigned int j=0;

for(;DelayTime>0;DelayTime——)

?邀for(j=0;j<125;j++)?邀;?妖?妖?妖

这其中定义了一个变量j,定义的方法是unsigned int j=0;如果将这个定义改一下:

unsigned char j;

结果又会如何呢?实际执行一下这个程序,会发现延迟的时间不一样了。

程序设计中的数据有着很多种不同的含义,不同含义的数据往往以不同的形式表现出来,这些数据在计算机内部进行处理、存储时有很大的区别,上述例子就是改变了数据类型的定义而发生的变化。

数据类型概述

C语言中常用的数据类型有:整型、字符型、实型等。

C语言中数据有常量与变量之分,它们分别属于以上这些类型。由以上这此数据类型还可以构成更复杂的数据结构,在程序中用到的所有的数据都必须为其指定类型。

1.常量

在程序运行过程中,其值不能被改变的量称为常量。

使用常量时可以直接给出常量的值,也可以用一些符号来替代常量的值,这称之为“符号常量”,下面通过一个例子介绍符号常量的用法及用途。

例1:在P1口接有8个LED,要求点亮P1.0所接LED。


程序实现:
参考图1,建立工程并输入源程序,设置工程,用实验仿真板演示,请读者自行完成。

图1
图1 🔍原图 (217×294)

程序分析:
程序中用#define LIGHT0 0xfe来定义符号LIGHT0,以后程序中所有出现 LIGHT0的地方均会用0xfe来替代,因此,这个程序执行结果就是P1=0xfe,即接在P1.0引脚上的LED点亮。

使用符号常量的好处是:

1.含义清楚。在我们单片机程序中,常有一些量是具有特定含义的,如某单片机系统扩展了一些外部芯片,每一块芯片的地址即可用符号常量定义,如:

#define PORTA 0x7fff

#define PORTB 0x7ffe

程序中可以用PORTA、PORTB来对端口进行操作,而不必写0x7fff、0x7ffe。显然,这两个符号比两个数字更能令人明白其含义。在给符号常量起名字时,尽量要做到“见名知意”,以充分发挥这一特点。

2.在需要改变一个常量时能做到“一改全改”。如果由于某种原因,端口的地址发生了变化(如修改了硬件),由0x7fff改成了0x3fff,那么只要将所定义的语句改动为:#define PORTA 0x3fff即可,不仅方便,而且能避免出错。

符号常量不等同于变量,它的值在整个作用域范围内不能改变,也不能被再次赋值。比如下面的语句是错误的:

LIGHT=0x01;

2.变量

值可以改变的量称为变量。一个变量应该有一个名字,在内存中占据一定的存储单元,在该存储单元中存放变量的值。请注意变量名与变量值的区别,下面从汇编语言的角度对此作一个解释。使用汇编语言编程时,必须自行确定RAM单元的用途,如某仪表有4位LED数码管,编程时将3CH~3FH作为显示缓冲区,当要显示一个字串“1234”时,汇编语言可以这样写:

MOV 3CH,#1

MOV 3DH,#2

MOV 3EH,#3

MOV 3FH,#4

经过显示程序处理后,在数码管上显示1234。这里的3CH就是一个存储单元,而送到该单元中去的“1”是这个单元中的数值,显示程序中需要的是待显示的值“1”,但不借助于3CH又没有办法来用这个1,这就是“数”与该数所在地址单元的关系。同样,在高级语言中,变量名仅是一个符号,需要的是变量的值,但是不借助于该符号又无法用该值。实际上如果在程序中写上“x1=5;”这样的语句,经过C编译程序的处理之后,也会变成“MOV 3CH,#5”之类的语句,只是究竟是使用3CH还是其他如3DH,4FH等作为存放x1内容的单元,是由C编译器根据实际情况确定的。

用来标识变量名、符号常量名、函数名、数组名、类型名等的有效字符序列称为标识符。简单地说,标识符就是一个名字。C语言规定标识符只能由字母、数字和下划线三种字符组成,且第一个字符必须为字母或下划线,要注意的是C语言中大写字母与小写字母被认为是两个不同的字符,即Sum与sum是两个不同的标识符。

标准的C语言并没有规定标识符的长度,但是各个C编译器有自己的规定,在Keil C编译器中可以使用长达数十个字符的标识符。在C语言中,要求对所有用到的变量作强制定义,也就是“先定义,后使用”。

3.常量与变量的区别

前面的课程中多次用到延时程序,其中调用延时程序是这么写的:

mDelay(1000);

这其中括号中参数1000决定了灯流动的速度,这个1000就是常量。显然,该数据不能在现场修改,如果有人提出希望改变流水灯的速度,那么只能重新编程、写片才能更改。

如果在现场有修改流水灯速度的要求,括号中就不能写入一个常数,为此可以定义一个变量(如Speed),写程序时这么写:mDelay(Speed);然后再编写一段程序,使得Speed的值可以通过按键被修改,流水灯的速度就可以在现场修改了,这时就需要用到变量了。

两种常用数据类型

1.整型数据


(1)整型数据在内存中的存放形式

如果定义了一个int型变量i:

int i=10; /*定义i为整型变量,并将10赋给该变量*/

在Keil C中规定使用两个字节表示int型数据,因此,变量i在内存中的实际占用情况如下:

0,0000,0000,1010

也就是整型数据总是用2个字节存放,不足部分用0补齐。


(2)整型变量的分类

整型变量的基本类型是int,可以加上有关数值范围的修饰符。这些修饰符分两类,一类是short和long,另一类是unsigned,这两类修饰符可以同时使用。

在int前加上short或long是表示数的大小的,对于Keil C来说,加short和不加short是一模一样的(在有一些C语言编译系统中是不一样的),所以,short就不讨论了。如果在int前加上long的修饰符,那么这个数就被称之为长整数,在Keil C中,长整数要用4个字节来存放,而基本int型用2个字节。显然,长整数所能表达的范围比整数要大,一个长整数表达的范围可以有:

-2\(^{31}\)<x<2\(^{31}\)-1

而不加long修饰的int型数据的范围是-32768~32767,可见,二者相差很远。

第二类修饰符是unsigned即无符号的意思,加上这个修饰符,说明其后的数是一个无符号的数。无符号、有符号的差别还是数的范围不一样。对于un-signed int而言,仍是用2个字节(16位)表示一个数,但其数的范围是0~65535,对于unsigned long int而言,仍是用4个字节(32位)表示一个数,但其数的范围是0~2\(^{32}\)-1。

2.字符型数据


(1)字符型数据在内存中的存放形式

数据在内存中是以二进制形式存放的,如果定义了一个char型变量c:

char c=10; /*定义c为字符型变量,并将10赋给该变量*/

十进制数10的二进制形式为1010,在Keil C中规定使用一个字节表示char型数据,因此,变量c在内存中的实际占用情况如下:

0,1010

弄明白了整型数据和字符型数据在内存中的存放,两者在前述程序中引起的差别就不难理解了,当使用int型变量时,程序需要对16位二进制码运算,而80C51是8位机,一次只能处理8位二进制码,所以就要分次处理,因此延迟时间就变长了。


(2)字符型变量的分类

字符型变量只有一个修饰符“unsigned”。对于一个字符型变量来说,其表达的范围是-128~+127,而加上了unsigned后,其表达的范围变为0~255。

使用Keil C时,不论是char型还是int型,我们都非常喜欢用unsigned型的数据,这是因为在处理有符号的数时,程序要对符号进行判断和处理,运算的速度会减慢,对单片机而言,速度比不上PC机,又工作于实时状态,任何提高效率的方法都要考虑。


(3)字符的处理

在一般的C语言中,字符型变量常用处理字符,如:char c=“a”,即是定义一个字符型的变量c,然后将字符a赋给该变量。进行这一操作时,实际是将字符a的ASCII码值赋给变量c,因此,做完这一操作之后,c的值是97。

既然字符最终也是以数值来存储的,那么和int i=97究竟有多大的区别呢?实际上它们是非常类似的,区别仅仅在于i是16位的,而c是8位的,当i的值不超过255时,两者完全可以互换,C语言字符型数据作这样的处理使用得程序设计时增大了自由度。

在单片机的C语言程序设计中,往往把字符型变量当成一个“8位的整型变量”来用,即在需要数学计算的场合,只要预知其范围不会超过8位所能表示的范围,就用char的数据来表示。

数的溢出

一个字符型数的最大值是127,一个整型数的最大值是32767,如果再加1,会出现什么情况呢?下面用一个例子来说明。

例2:演示字符型数据和整型数据溢出的例子。


程序实现:
参考图2输入源程序,建立名为exam42的工程,设置工程,选择“C51”选项卡,在Code Optimizationk中的level将优化级别设为0,即0:Con-tanst folding,避免C编译器的自动优化使我们不能得到想要的结果。编译、连接后,运行,查看变量,结果是b和d在加1之后分别变成了0和-32768,这是为什么呢?


程序分析:
首先看变量a,该变量的值是255,无符号字符型数据,因此,该变量在内存中以8位(一个字节)来存放,将255转化为二进制即1111,1111,如果将该值加1,结果是1,0000,0000,由于该变量只能存放8位,所以最高位的1丢失,于是该数字就变成了0000,0000,自然就是十进制的0了。其实这不难理解,录音机上有磁带计数器,共有3位,当转到999后,再转一圈,本应是1000,但实际看到的是000,除非借助于其他方法,否则无法判断其是转了1000圈还是根本没有动。

通过实验可以看出,在出现这样的问题时C编译系统不会给出提示(其他语言中BASIC等会报告出错),这有利于编出灵活的程序来,但也会引起一些副作用,这就要求C程序员对硬件知识有较多的了解,对于数在内存中的存放等基本知识必须清楚。

(周坚)