第十课 数组的使用
前面的文章中,都是介绍单个数据变量的使用,在“走马灯”等的例子中略有使用到数组,不难看出,数组不过就是同一类型变量的有序集合。形象的可以这样去理解,就像一个学校在操场上排队,每一个级代表一个数据类型,每一个班级为一个数组,每一个学生就是数组中的一个数据。数据中的每个数据都可以用唯一的下标来确定其位置,下标可以是一维或多维的。就如在学校的方队中要找一个学生,这个学生在I 年级H 班X 组Y 号的,那么可以把这个学生看做在I 类型的H 数组中(X,Y)下标位置中。数组和普通变量一样,要求先定义了才可以使用,下面是定义一维或多维数组的方式:
一维 数据类型 数组名 [常量表达式];
多维 数据类型 数组名 [常量表达式1]...... [,常量表达式N];
“数据类型”是指数组中的各数据单元的类型,每个数组中的数据单元只能是同一数据类型。“数组名”是整个数组的标识,命名方法和变量命名方法是一样的。在编译时系统会根据数组大小和类型为变量分配空间,数组名可以说就是所分配空间的首地址的标识。“常量表达式”是表示数组的长度和维数,它必须用“[]”括起,括号里的数不能是变量只能是常量。
unsigned int xcount [10]; //定义无符号整形数组,有10 个数据单元
char inputstring [5]; //定义字符形数组,有5 个数据单元
float outnum [10],[10]; //定义浮点型数组,有100 个数据单元
在C 语言中数组的下标是从0 开始的而不是从1 开始,如一个具有10 个数据单元的数组count,它的下标就是从count[0]到count[9],引用单个元素就是数组名加下标,如count[1]就是引用count 数组中的第2 个元素,如果错用了count[10]就会有错误出现了。还有一点要注意的就是在程序中只能逐个引用数组中的元素,不能一次引用整个数组,但是字符型的数组就可以一次引用整个数组。
数组也是可以赋初值的。在上面介绍的定义方式只适用于定义在内存DATA 存储器使用的内存,有的时候我们需要把一些数据表存放在数组中,通常这些数据是不用在程序中改变数值的,这时就要把这些数据在程序编写时就赋给数组变量。因为51 芯片的片内RAM很有限,通常会把RAM 分给参与运算的变量或数组,而那些程序中不变数据则应存放在片内的CODE 存储区,以节省宝贵的RAM。赋初值的方式如下:
数据类型 [存储器类型] 数组名 [常量表达式] = {常量表达式};
数据类型 [存储器类型] 数组名 [常量表达式1].... [,常量表达式N]={{常量表达
式}...{,常量表达式N}};
在定义并为数组赋初值时,初学的朋友往往会搞错初值个数和数组长度的关系,而致使编译出错。初值个数必须小于或等于数组长度,不指定数组长度则会在编译时由实际的初值个数自动设置。
unsigned char LEDNUM[2]={12,35}; //一维数组赋初值
int Key[2][3]={{1,2,4},{2,2,1}}; //二维数组赋初值
unsigned char IOStr[]={3,5,2,5,3}; //没有指定数组长度,编译器自动设置
unsigned char code skydata[]={0x02,0x34,0x22,0x32,0x21,0x12}; //数据保存在code 区
下面的一个简单例子是对数组中的数据进行排序,使用的是冒泡法,一来了解数组的使用,二来掌握基本的排序算法。冒泡排序算法是一种基本的排序算法,它每次顺序取数组中的两个数,并按需要按其大小排列,在下一次循环中则取下一次的一个数和数组中下一个数进行排序,直到数组中的数据全部排序完成。
#include
#include
void taxisfun (int taxis2[])
{
unsigned char TempCycA,TempCycB,Temp;
for (TempCycA=0; TempCycA<=8; TempCycA++)
{
//共10个数据排序,外循环共需比较10-1=9次,故循环为0~8 。
for (TempCycB=0; TempCycB<=8-TempCycA; TempCycB++)
//1、每个大循环内,共要比较 8-TempCycA 次,并把最小的数放在最后
//2、TempCycB<8-TempCycA 比用TempCycB<=8 少做很多循环
{
if (taxis2[TempCycB+1]>taxis2[TempCycB]) //当后一个数大于前一个数
{ //按从大到小排列
Temp = taxis2[TempCycB]; //前后2 数交换
taxis2[TempCycB] = taxis2[TempCycB+1];
taxis2[TempCycB+1] = Temp;
//因函数参数是数组名调用形参的变动影响实参
}
}
}
}
void main(void)
{
int taxis[] = {113,5,22,12,32,233,1,21,129,3}; //共10个数据
char Text1[] = {"source data:"}; //"源数据"
char Text2[] = {"sorted data:"}; //"排序后数据"
unsigned char TempCyc;
SCON = 0x50; //串口方式1,允许接收
TMOD = 0x20; //定时器1 定时方式2
TCON = 0x40; //设定时器1 开始计数
TH1 = 0xE8; //11.0592MHz 1200 波特率
TL1 = 0xE8;
TI = 1;
TR1 = 1; //启动定时器
printf("%s\n",Text1); //字符数组的整体引用,“源数据”
// %s:输出函数格式字符,指向一个带结束符的字符串
// “\n”:常用转义字符,表示换行符LF
for (TempCyc=0; TempCyc<10; TempCyc++)
printf("%d ",taxis[TempCyc]);
printf("\n----------\n");
taxisfun (taxis); //以实际参数数组名taxis 做参数被函数调用
printf("%s\n",Text2); //字符数组的整体引用,“排序后数据”
for (TempCyc=0; TempCyc<10; TempCyc++) //调用taxisfun ()后,taxis 会被改变
printf("%d ",taxis[TempCyc]); // %d:输出函数格式字符,有符号十进制数
while(1);
}
例子中可以看出,数组同样可以作为函数的参数进行传递。数组做参数时是用数组名进行传递的,一个数组的数组名表示该数组的首地址,在用数组名作为函数的调用参数时,它的传递方式是采用了地址传递,就是将实际参数数组的首地址传递给函数中的形式参数数组,这时实际参数数组和形式参数数组实际上是使用了同一段内存单元,当形式参数数组在函数体中改变了元素的值,同时也会影响到实际参数数组,因为它们是存放在同一个地址的。
上面的例子同时还使用到字符数组。字符数组中每一个数据都是一个字符,这样一个一维的字符数组就组成了一个字符串,在C 语言中字符串是以字符数组来表达处理的。为了能测定字符串的长度,C 语言中规定以‘\o’来做为字符串的结束标识,编译时会自动在字符串的最后加入一个‘\o’,那么要注意的是如果用一个数组要保存一个长度为10 字节的字符串则要求这个数组至少可以保存11 个元素。‘\o’是转义字符,它的含义是空字符,它的ASCII 码为00H,也就是说当每一个字符串都是以数据00H 结束的,在程序中操作字符数据组时要注意这一点。字符数组除了可以对数组中单个元素进行访问,还可以访问整个数组,其实整个访问字符数组就是把数组名传到函数中,数组名是一个指向数据存放空间的地址指针,函数根据这个指针和‘/o’就可以完整的操作这个字符数组。对于这一段所说的,可以参看下面一例1602LCD 显示模块的驱动演示例子进行理解。这里要注意就是可以用单个字符数组元素来进行运算,但不能用整个数组来做运算,因为数组名是指针而不是数据。
/*============================================================
使用1602 液晶显示的实验例子 明浩 2004/2/27
==============================================================
SMC1602A(16*2)模拟口线接线方式
连接线图:
---------------------------------------------------
|LCM-----51 | LCM-----51 | LCM------51 |
---------------------------------------------|
|DB0-----P1.0 | DB4-----P1.4 | RW-------P2.0 |
|DB1-----P1.1 | DB5-----P1.5 | RS-------P2.1 |
|DB2-----P1.2 | DB6-----P1.6 | E--------P2.2 |
|DB3-----P1.3 | DB7-----P1.7 | VLCD 接1K 电阻到GND|
---------------------------------------------------
[注:AT89S51 使用12M 晶振]
=============================================================*/
#define LCM_RW P2_0 //定义引脚
#define LCM_RS P2_1
#define LCM_E P2_2
#define LCM_Data P1
#define Busy 0x80 //用于检测LCM 状态字中的Busy 标识
#include
void WriteDataLCM(unsigned char WDLCM);
void WriteCommandLCM(unsigned char WCLCM,BuysC);
unsigned char ReadDataLCM(void);
unsigned char ReadStatusLCM(void);
void LCMInit(void);
void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData);
void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData);
void Delay5Ms(void);
void Delay400Ms(void);
unsigned char code cdle_net[] = {"www.cdle.net"};
unsigned char code email[] = {"pnzwzw@cdle.net"};
void main(void)
{
Delay400Ms(); //启动等待,等LCM 讲入工作状态
LCMInit(); //LCM 初始化
Delay5Ms(); //延时片刻(可不要)
DisplayListChar(0, 0, cdle_net);
DisplayListChar(0, 1, email);
ReadDataLCM();//测试用句无意义
while(1);
}
//写数据
void WriteDataLCM(unsigned char WDLCM)
{
ReadStatusLCM(); //检测忙
LCM_Data = WDLCM;
LCM_RS = 1;
LCM_RW = 0;
LCM_E = 0; //若晶振速度太高可以在这后加小的延时
LCM_E = 0; //延时
LCM_E = 1;
}
//写指令
void WriteCommandLCM(unsigned char WCLCM,BuysC) //BuysC 为0 时忽略忙检测
{
if (BuysC) ReadStatusLCM(); //根据需要检测忙
LCM_Data = WCLCM;
LCM_RS = 0;
LCM_RW = 0;
LCM_E = 0;
LCM_E = 0;
LCM_E = 1;
}
//读数据
unsigned char ReadDataLCM(void)
{
LCM_RS = 1;
LCM_RW = 1;
LCM_E = 0;
LCM_E = 0;
LCM_E = 1;
return(LCM_Data);
}
//读状态
unsigned char ReadStatusLCM(void)
{
LCM_Data = 0xFF;
LCM_RS = 0;
LCM_RW = 1;
LCM_E = 0;
LCM_E = 0;
LCM_E = 1;
while (LCM_Data & Busy); //检测忙信号
return(LCM_Data);
}
void LCMInit(void) //LCM 初始化
{
LCM_Data = 0;
WriteCommandLCM(0x38,0); //三次显示模式设置,不检测忙信号
Delay5Ms();
WriteCommandLCM(0x38,0);
Delay5Ms();
WriteCommandLCM(0x38,0);
Delay5Ms();
WriteCommandLCM(0x38,1); //显示模式设置,开始要求每次检测忙信号
WriteCommandLCM(0x08,1); //关闭显示
WriteCommandLCM(0x01,1); //显示清屏
WriteCommandLCM(0x06,1); // 显示光标移动设置
WriteCommandLCM(0x0C,1); // 显示开及光标设置
}
//按指定位置显示一个字符
void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData)
{
Y &= 0x1;
X &= 0xF; //限制X 不能大于15,Y 不能大于1
if (Y) X |= 0x40; //当要显示第二行时地址码+0x40;
X |= 0x80; //算出指令码
WriteCommandLCM(X, 0); //这里不检测忙信号,发送地址码
WriteDataLCM(DData);
}
//按指定位置显示一串字符
void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData)
{
unsigned char ListLength;
ListLength = 0;
Y &= 0x1;
X &= 0xF; //限制X 不能大于15,Y 不能大于1
while (DData[ListLength]>0x20) //若到达字串尾则退出
{
if (X <= 0xF) //X 坐标应小于0xF
{
DisplayOneChar(X, Y, DData[ListLength]); //显示单个字符
ListLength++;
X++;
}
}
}
//5ms 延时
void Delay5Ms(void)
{
unsigned int TempCyc = 5552;
while(TempCyc--);
}
//400ms 延时
void Delay400Ms(void)
{
unsigned char TempCycA = 5;
unsigned int TempCycB;
while(TempCycA--)
{
TempCycB=7269;
while(TempCycB--);
};
}
本文来源:https://www.2haoxitong.net/k/doc/7002fc039a6648d7c1c708a1284ac850ac02046b.html
文档为doc格式