|
单片机c语言,10小时学会C 语言 (七)
第七章 数组与指针
数组是由一群相同型态的变量所组成,当你需要一堆相同型态的变量时,用数组是最适合的。例如,要储存全班 50 位同学的成绩,那就需要 50 个变量,如果要一个一个宣告,那就太没有效率了。这时就需要用数组了。指针则是指向变量或数组地址的运算子,任一变量或数组的数值均可经由指标的运算获得。
◎ 数组的宣告
数组宣告的格式如下:
┌─────────────────────────────────┐
│变量型别 变量名称[元素个数]; │
└─────────────────────────────────┘
以储存全班 50 位同学的成绩为例,用整数来存成绩:
┌────────────────────────────────────┐
│int score[50]; │
└────────────────────────────────────┘
C 语言中,数组的索引值是由 0 开始的,所以这 50 个变量为:score[0], score[1], score[2], ..., score[48], score[49] 。
以下的范例:读取一周的气温,再求出其平均值。
┌────────────────────────────────────────────────────────────────┐
│temper.c │
├────────────────────────────────────────────────────────────────┤
1│#include/* 宣告 printf(),scanf() 的原型 */ │
2│void main(void) │
3│{ │
4│ int t[7]; │
5│ int day, sum; │
6│ │
7│ for( day = 0 ; day < 7 ; day++ ) │
8│ { │
9│ printf("Enter the temperature for day %d : ", day+1 ); │
10│ scanf("%d", &t[day]); │
11│ } │
12│ │
13│ sum = 0; │
14│ for( day = 0 ; day < 7 ; day++ ) │
15│ sum = sum + t[day]; │
16│ │
17│ printf("Average temperature = %f\n", sum/7. ); │
18│} │
├────────────────────────────────────────────────────────────────┤
│Enter the temperature for day 1 : 22 │
│Enter the temperature for day 2 : 20 │
│Enter the temperature for day 3 : 18 │
│Enter the temperature for day 4 : 19 │
│Enter the temperature for day 5 : 23 │
│Enter the temperature for day 6 : 24 │
│Enter the temperature for day 7 : 26 │
│Average temperature = 21.714286 │
└────────────────────────────────────────────────────────────────┘
在第四章中,我们曾经列出各种变量型态的指针,以上面的例子:
位 址 记 忆 体 指 标 假设地址
├────────────────┤
&t[0] ───→│ t[0] │←── t + 0 ( 1000 )h
├────────────────┤
&t[1] ───→│ t[1] │←── t + 1 ( 1002 )h
├────────────────┤
&t[2] ───→│ t[2] │←── t + 2 ( 1004 )h
├────────────────┤
&t[3] ───→│ t[3] │←── t + 3 ( 1006 )h
├────────────────┤
&t[4] ───→│ t[4] │←── t + 4 ( 1008 )h
├────────────────┤
&t[5] ───→│ t[5] │←── t + 5 ( 100A )h
├────────────────┤
&t[6] ───→│ t[6] │←── t + 6 ( 100C )h
├────────────────┤
我们利用第 10 行 scanf("%d", &t[day]); 读取数据,这里用的是每一个数组元素的地址 &t[day] ,day 由 0 到 6 。
我们也可以改用指标的方式: scanf("%d", t+day );这里的 t 是数组变量的名称,也就一个指标常数(constant pointer),在宣告数组变量的同时,就宣告了这个指标常数,只是这个指标常数的数值不是由你决定的,而是在程序执行时,它的数值才会确定,一但确定,就不会改变。t + day 的 day 是 t 指标的增加值,假设 t 为 ( 1000 )h,那 t + 1 是 ( 1001 )h 吗?
C 语言在作指针运算时,会依指标的型别不同,而改变递增或递减的数值。以 t + 1 来说, t 是一个整数的指标常数,在 TC2 中 一个整数占有 2 bytes,所以 t + 1 的数值是 ( 1002 )h ,而不是 ( 1001 )h 。同理,t + 2 的数值是 ( 1004 )h 。
在上例第 17 行, printf("Average temperature = %f\n", sum/7. );不知道你有没有注意到 sum/7. 在 7 后面的一点 ( . ) ?
这并不是Keyin 错误,而是故意的。因为 sum 是一个整数变量,若写成 sum/7 表示整数除以整数,那结果还是整数。故意写成 sum/7.就变成整数除以实数,那结果会是实数。即然是平均值,通常会有小数,所以我们用 %f 来秀出数据。
◎ 字符串
字符串是一堆字符所组成的,它是一个字符数组,只是字符串它还要有个 '\0' 字符,作为字符串结束记号。例如,我们要用一个字符串存 DOS 的文件名:DOS 的檔名是主檔名最多 8 个字符,扩展名最多 3 个字符,中间以句号 ( . )分隔,所以需要 8 + 3 + 1 = 12 个字符来存盘名。但是别忘了字符串要有 '\0'的结束字符,所以,总共需要 12 + 1 = 13 个字符:
char filename[13];
你可以用以下的程序片断来询问文件名:
printf("Enter the file name : ");
scanf("%12s", filename );
scanf 是要变量的地址,而 filename[13] 这个变量的地址就是 filename ,你也可以用 &filename[0] 。另外,用 %12s 这个句柄,表示读入的字符串只取前面 12 个字符再加上 '\0' 字符,传给后面所指定的地址。如果你不加, 12 而用 %s 的话,当使用者输入超过 13 个字符时,将会发生不可预期的后果。
以下的范例:读取一个字符串并作输出。
┌────────────────────────────────────────────────────────────────┐
│string.c │
├────────────────────────────────────────────────────────────────┤
1│#include/* 宣告 printf(),scanf() 的原型 */ │
2│void main(void) │
3│{ │
4│ char name[13]; │
5│ │
6│ printf("Enter your name please : "); │
7│ scanf("%12s", name ); │
8│ printf("Good day, Mr. %s.", name ); │
9│} │
├────────────────────────────────────────────────────────────────┤
│Enter your name please : Lee │
│Good day, Mr. Lee. │
└────────────────────────────────────────────────────────────────┘
你可以将第 7 行的 %12s 改为 %s ,并且在执行程序时,输入一个较长的字符串,看看会发生什么事?
◎ 二维及多维数组
C 语言除了一维数组外,也可以依需要宣告二维或以上的变量数组:
┌──────────────────────────────────────────────────────────────┐
│变数型别 变量名称[第一维元素个数][第二维元素个数]; │
└──────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│变数型别 变量名称[第一维元素个数][第二维元素个数][第三维元素个数]; │
└────────────────────────────────────────────────────────────┘
不过会用到三维数组以上的机会蛮少的。以计算学生成绩为例,用一维数组 int score[50]; 我们只能记录一科的成绩,用二维数组 int score[50][7]; 就能记录七科的成绩,或者是记录单一科目的期中考、期末考、平常成绩、作业成绩等等。
以下是课本的范例(加上范围判断):秀出使用者所指定坐标的位置。
┌────────────────────────────────────────────────────────────────┐
│demo.c │
├────────────────────────────────────────────────────────────────┤
1│#include/* 宣告 printf(),scanf() 的原型 */ │
2│void main(void) │
3│{ │
4│ char matrix[5][10]; │
5│ int x,y; │
6│ │
7│ for( y=0 ; y<5 ; y++ ) /* 设定数组初值 */ │
8│ for( x=0 ; x<10 ; x++) │
9│ matrix[y][x] = '.' ; │
10│ │
11│ printf("Enter the coordinate(0 0) - (9 4) : "); │
12│ scanf("%d %d", &x, &y ); /* 读取指定坐标 */ │
13│ │
14│ for( ; !( ( x>=0 || x<=9 ) && ( y>=0 || y<=4 ) ) ; ) │
15│ { │
16│ printf("\aInvalid Value!!\n"); │
17│ printf("Please enter the coordinate(0 0) - (9 4) : "); │
18│ scanf("%d %d", &x, &y ); /* 读取指定坐标 */ │
19│ } │
20│ matrix[y][x] = '*' ; /* 设定指定坐标 */ │
21│ │
22│ for( y=0 ; y<5 ; y++ ) /* 秀出数组值 */ │
23│ { │
24│ for( x=0 ; x<10 ; x++) │
25│ printf("%c", matrix[y][x] ); │
26│ printf("\n"); │
27│ } │
28│} │
├────────────────────────────────────────────────────────────────┤
│Enter the coordinate(0 0) - (9 4) : 5 3 │
│.......... │
│.......... │
│.......... │
│.....*.... ← 因为索引值是由 0 开始所以 * 在 6,4 │
│.......... │
└────────────────────────────────────────────────────────────────┘
第 14 行到第 19 行是利用一个 for 循环来判断输入的数值是否在要求的范围。在使用数组时,最好要注意一下使用索引值的范围,因为 C 不会做数组边界检查,当你使用大于你所宣告的索引值时,在语法上,并不会有任何的错误。但是当你使用索引值超过你所宣告的的数组时,你有可能改变到别的变量,甚至是程序代码。轻微的话,只是程序结果错误,严重的话,可能导致当机。
☆ 两维数组的指针 ☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
以 int s[50][3]; 为例
位 址 记 忆 体 指 标 假设状况
├────────────┤
&s[0][0] →│ s[0][0] │← *(s + 0) + 0 ← s + 0 ← s ( 1000 )h
├────────────┤
&s[0][1] →│ s[0][1] │← *(s + 0) + 1 ( 1002 )h
├────────────┤
&s[0][2] →│ s[0][2] │← *(s + 0) + 2 ( 1004 )h
├────────────┤
&s[1][0] →│ s[1][0] │← *(s + 1) + 0 ← s + 1 ( 1006 )h
├────────────┤
&s[1][1] →│ s[1][1] │← *(s + 1) + 1 ( 1008 )h
├────────────┤
&s[1][2] →│ s[1][2] │← *(s + 1) + 2 ( 100A )h
├────────────┤
&s[2][0] →│ s[2][0] │← *(s + 2) + 0 ← s + 2 ( 100C )h
├────────────┤
... ... ...
├────────────┤
&s[48][2] →│ s[48][2] │← *(s +48) + 2 (30h*3+2)*2 ( 1124 )h
├────────────┤
&s[49][0] →│ s[49][0] │← *(s +49) + 0 ← s +49 ( 1126 )h
├────────────┤
&s[49][1] →│ s[49][1] │← *(s +49) + 1 ( 1128 )h
├────────────┤
&s[49][2] →│ s[49][2] │← *(s +49) + 2 ( 112A )h
├────────────┤
s 是一个二维指标常数。
一个一维指标,我们必须在指标前加一个星号 ( * ) 才能取得一维数组内的变量值,同样地,在二维指标,我们必须在指标前加二个星号,才能取得二维数组内的变数值。若是二维指标前的星号少于二个,例如,没有星号或只有一个,那它所表示的就还是一个指标。另外,二维指标前如果有一个星号,那就相当是降了一维,而成为一个一维指标。
○ 二维指标的递增值
一个一维指标,如果它所指的变量型态占有 n 个 byte ,那这个一维指标的递增值就是 n 。
一个二维数组: vartype Array[ X ][ Y ]; 如果 vartype 占有 n 个 byte,那Array 这个二维指标的递增值就是 Y * n ,也就是 Y 个 vartype 所占的空间。Array 是指向二维数组的开端 &Array[0][0],而 Array+1 正好指向 &Array[1][0],同理 Array+i 是指向 &Array[i][0]。
如前所述,在二维指标前加一个星号就会变成一维指标。所以, *Array 是个一维指标、*(Array+1) 是一维指标、...、*(Array+i) 是一维指标、...、*(Array+(X-1)) 是一维指标,而这些一维指标都是指向第二维数组开始的地址。
其中的 0≦ i ≦(X-1),如同决定 Array[X][Y] 中第一维(X)的位移量(Offset)。这些一维指标的递增值 *(Array+i)、...、*(Array+i)+j、...、*(Array+i)+(Y-1)就指向第二维的每一个变量。
其中的 0≦ j ≦(Y-1),如同决定 Array[X][Y] 中第二维(Y)的位移量(Offset)。所以, *(Array + i) + j 就是指向 Array[i][j] 这个变量,同时*( *(Array + i) + j ) 就是 Array[i][j] 这个变量。
★ C 语言不会作数组边界(Boundary)检查,例如,你宣告 int s[50][7];可是你可以使用 s[0][8]、s[51][2] 或 s[100][200]。
○ 用指标的方式,修改上例,你看懂了吗?
┌────────────────────────────────────────────────────────────────┐
│demoptr.c │
├────────────────────────────────────────────────────────────────┤
1│#include/* 宣告 printf(),scanf() 的原型 */ │
2│void main(void) │
3│{ │
4│ char matrix[5][10]; │
5│ int i,x,y; │
6│ │
7│ for( i=0 ; i<5*10 ; i++ ) /* 设定数组初值 */ │
8│ *((*matrix)+i) = '.' ; │
9│ │
10│ printf("Enter the coordinate(0 0) - (9 4) : "); │
11│ scanf("%d %d", &x, &y ); /* 读取指定坐标 */ │
12│ │
13│ for( ; !( ( x>=0 || x<=9 ) && ( y>=0 || y<=4 ) ) ; ) │
14│ { │
15│ printf("\aInvalid Value!!\n"); │
16│ printf("Please enter the coordinate(0 0) - (9 4) : "); │
17│ scanf("%d %d", &x, &y ); /* 读取指定坐标 */ │
18│ } │
19│ matrix[y][x] = '*' ; /* 设定指定坐标 */ │
20│ │
21│ for( y=0 ; y<5 ; y++ ) /* 秀出数组值 */ │
22│ { │
23│ for( x=0 ; x<10 ; x++) │
24│ printf("%c", *( *(matrix + y) + x) ); │
25│ printf("\n"); │
26│ } │
27│} │
├────────────────────────────────────────────────────────────────┤
└────────────────────────────────────────────────────────────────┘
单片机教程,五系列(55讲)电子书全集下载论坛精选:
■ 单片机c语言,10小时学会C 语言 (一)
第一章 C 语言简介与Turbo C 的使用
■ 单片机c语言,10小时学会C 语言 (二)
第二章 C 程序的结构
■ 单片机c语言,10小时学会C 语言 (三)
第三章 常数与变数
■ 单片机c语言,10小时学会C 语言 (四)
第四章 基本输出入函式
■ 单片机c语言,10小时学会C 语言 (五)
第五章 流程图与抉择指令
■ 单片机c语言,10小时学会C 语言 (六)
第六章 循环与自动重复
■ 单片机c语言,10小时学会C 语言 (七)
第七章 数组与指针
■ 单片机c语言,10小时学会C 语言 (八)
第八章 函数与呼叫
■ 单片机c语言,10小时学会C 语言 (九)
第九章 档案存取
sadasdasdas
dsf
九
r
很好啊
好
zz


