1、第4章 数组,1.,2.,3.,本章讲述内容:,一维数组 ;,二维数组 ;,字符数组和字符串 。,给出数组的名字,用它来代表这些数据的整体。为数组起名字,应该符合C语言对标识符的规定。,4.1 数组的基本概念,.,所谓“数组”,是用一个名字去代表相同数据类型元素的有序集合,用对应的序号来区分这个集合中的一个个元素。所起的名字,称为“数组名”,序号称为数组元素的“下标”。,.,用一个下标来区分其元素的数组,称为“一维数组”;用两个或多个下标来区分其元素的数组,称为“二维数组”或“多维数组”。,.,在程序中说明一个数组后,系统就为它在内存分配一个连续的存储区,顺序存放该数组中的元素。这个存储区所需
2、要的字节数,按如下公式计算:总字节数 = 数组元素个数 数据类型长度,.,数组说明向系统传达的信息,(1),(2),指明数组元素的数据类型。,(3),确定数组的大小,即该数组包含的元素个数,得到所需连续存储区的规模。,.,当数据类型一样时,数组说明和变量说明可以混同在一个语句里。也就是说,只要数据类型相同,一个说明语句里,既可以有变量说明,也可以有数组说明,它们的中间用逗号隔开即可。,比如,语句:int array 8; 说明一个名为array的整型数组,它有8个元素,每个元素都是int型的变量。这8个元素各自的名称是:array0,array1,array2,array3,array4,ar
3、ray5,array6,array7,4.2 一维数组,4.2.1 一维数组的说明,.,说明一维数组的语句格式是:,.,.,C语言数组元素下标从0开始。本书约定,称array0 为第1个元素,array1为第2个元素,array2为第3个元素,如此等等。,.,C语言规定,数组名就是分配给该数组的存储区的起始地址。也就是说,一维数组的名字不是变量,而是一个内存地址常量(无符号数),只有它的元素才是变量。, ;,可以是auto(自动型)、static(静态型),存储类型省略时,默认是auto型的;可以是基本类型,也可是指针型、结构型等;是符合标识符规定的名字;是用方括号括住的整型常量,数值是该数组
4、拥有的元素个数。,.,在程序中说明有5个元素的一个整型数组ab5,打印输出数组存储区的首地址和各元素的地址。,例:,#include “stdio.h“main()int ab5;int j;printf (“ab = %un“, ab);for(j = 0; j5; j+)printf (“ ,(1),程序实现,(2),分析与讨论,地址是一个无符号数,程序中要打印变量的地址时,在printf中应该使用格式符“%u”。,.,按C语言的规定,数组名ab是分配给该数组的存储区起始地址。所以,直接把它以“%u”格式打印出来即可。数组的各元素是变量,它们的地址应在变量名前加取地址符&,即:&ab0,&
5、ab1,&ab2,&ab3,&ab4。,ab与&ab0的值是一样的。但含义不同,前者表示的是整个存储区的起址,后者仅是第1个元素(ab0)的地址。左边 是程序的运行结果和存 储分配的示意。,.,ab:65490,ab0,65492,ab1,65494,ab2,65496,ab3,65498,ab4,若数组说明时给出,但只依次为前几个元素赋了初值。那C语言将自动对余下元素赋初值:为数值型的赋0(或0.0);为字符型的赋空字符。,若说明时是对数组的所有元素赋初值,那在数组说明中可省略(方括号不能没有)。,4.2.2 一维数组元素的初始化,所谓一维数组的初始化,即指在说明数组的同时为其诸元素(变量)
6、赋初值。完整的数组说明语句格式为:,.,.,比如,有如下数组说明语句:float f4 = 0.1, 1.1, 2.1, 3.1; 表示名为f的数组有4个元素,存储类型是auto,数据类型是float,各元素的初值为:f0=0.1,f1=1.1,f2=2.1,f3=3.1,关于数组元素初始化的几点注意,.,(1),(2),(3),若数组的存储类型是static的,那么该数组所有元素都是静态(static)型变量。,(4),若数组说明时给出了,并对元素进行了初始化,那所列出的元素初始值的个数,不能多于数组元素的个数。否则C语言就会判定为语法错。, = , , ,;,其中是数组第1个元素的值,是数
7、组第2个元素的值,是数组第3个元素的值,如此等等。,例:,4.2.3 一维数组元素的引用,程序中绝不能用如下的赋值方式来达到为数组元素赋值的目的:int x10; /* 说明时,没对元素赋值 */x = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; /* 错误的语句 */x 10= 1, 2, 3, 4, 5, 6, 7, 8, 9, 10; /* 也是错误的语句 */,.,数组的每个元素是变量,它们可以接受赋值。程序中把数组的每个元素当成普通的变量来使用,这就是所谓的“数组元素的引用”。,.,编写程序,输入5个字符,然后按相反的次序输出。,#include “stdio.h“
8、 main() char ch1, ch2, ch3, ch4, ch5;scanf (“%c“, ,不用数组编程,利用数组和循环编程,printf (“%c“, ch5);printf (“%c“, ch4);printf (“%c“, ch3);printf (“%c“, ch2);printf (“%cn“, ch1); ,#include “stdio.h“ main() char ch5;int i;for (i=0; i=0; i-)printf (“%c“, chi);printf (“n“); ,(1),(2),编写程序,从键盘输入10个无序的整数,存放在数组s中。然后将它们由
9、小到大排好,加以输出。,例:,程序实现,分析与讨论,(1),(2),#include “stdio.h“main() int i, j, t, s10;printf (“Please enter 10 integers:n“);for (i=0; i10; i+)scanf (“%d“, ,.,程序中采用的排序方法是:若数组a有n个元素,那么共要做n1趟扫描(即是外循环)。第1趟扫描的范围是1n1(即用a0与a1an1两两比较,如果发现某个元素小于a0,就把它们的位置交换,最后致使a0中的值为最小者);第2趟扫描的比较范围是2n1(即用a1与a2an1两两比较,最后致使a1中的值是次小者);如
10、此等等。称这种排序方法为“冒泡排序”。,.,程序中元素的交换是通过临时变量t进行的。,.,C语言数组下标值范围是从0到1。不过它并不对数组元素的下标值做合法性检查。因此,程序员在程序设计时要特别注意,访问数组元素的下标时,越出了允许的范围(称为溢出),它就有可能侵入了别的数据的存储空间,或所取的数据不对,程序的运行肯定不会正确。,4.3 二维数组,.,说明一个二维数组的语句格式是:,.,比如,“int a34;”说明了名为a的二维整型数组:该数组共有3*4=12个元素,每个都是int型变量。第1个下标从0变到2,第2个下标从0变到3。这12个元素的名称是:a 00,a 01,a 02,a 03
11、a 10,a 11,a 12,a 13a 20,a 21,a 22,a 23,.,为处理二维数组,C语言先把二维数组看成是有这么多个元素的一维数组,每个元素的名为:0,1,1。然后再把该一维数组的每个元素看作是有这么多个元素的一维数组。,.,这样,数组a先视为有3个元素的一个一维数组,其元素名分别是:a0,a1,a2 (其实就是3行) 。随之,a0是有4个元素的一维数组,分别是:a 00,a 01,a 02,a 03; a1 是有4个元素的一维数组,分别是:a 10,a 11,a 12,a 13;a2 是有4个元素的一维数组,分别是:a 20,a 21,a 22,a 23。,4.3.1 二维数
12、组的说明, ;,其中:、与一维数组同。和是括在方括号里的整常量,其数值的乘积表示该数组所拥有的元素个数。,编写程序,打印出分配给说明的二维数组a的存储区的起始地址;打印出分配给3个一维数组a0,a1,a2 的存储区的起始地址;打印出二维数组各个元素的地址。,.,例:,程序实现,分析与讨论,(1),(2),#include “stdio.h“ main() int j, k, a34;printf (“a=%un“, a);for (j=0; j3; j+) printf (“a%d=%u “, j, aj); printf (“n“);for (j=0; j3; j+) for (k=0; k
13、4; k+)printf (“ ,程序运行结果如图所示,.,a是分配给该二维数组存储区的起始地址,a0是这个二维数组第1行a0所占用存储区的起始地址,而&a00是这个二维数组第1个元素的地址。所以它们3个都是65476。,.,对二维数组是按行分配存储的:先放 a 00,a 01,a 02,a 03;再放a 10,a 11,a 12,a 13;最后放a 20,a 21,a 22,a 23。,若是对二维数组的全部元素进行初始化,那在数组说明语句中,可以省略不写(方括号还是要的)。比如语句:int a 4= 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12; 等同于语句:i
14、nt a34=1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12; 或:int a34=1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;,分行对二维数组进行初始化。比如语句:int a34=1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12; 说明了一个名为a的int型二维数组,共有12个元素。,4.3.2 二维数组元素的初始化,在说明二维数组的同时,对每个元素赋予初始取值,这就是所谓的“二维数组的初始化”。,.,.,二维数组的初始化,有如下几种方法 :,(1),(2),不分行将所有数据依次列在一个花括号中。比如语句:
15、int a34=1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12;,(3),分行进行初始化时,可只对部分元素赋初值,剩余元素的初值由系统自动补齐:若是数值型的,就赋予0(或0.0);若是字符型的,就赋予空字符。比如语句:int a34=1, 4, 11;,(4),#include “stdio.h“main() int j, k;int new33, old33=1, 2, 3, 4, 5, 6, 7, 8, 9;printf (“The old array:n“);for (j=0; j3; j+) for (k=0; k3; k+) printf (“%4d“,
16、oldjk);newkj = oldjk;printf (“n“);,二维数组的每个元素都是变量,可以直接接受赋值。所以,在程序中通过向数组元素赋值的方法,就能使它们获得取值。把二维数组中的每个元素当成普通的变量来使用,这就是所谓的“二维数组元素的引用”。,.,4.3.3 二维数组元素的引用,例:,程序实现,分析与讨论,(1),(2),一个3行3列的二维数组,元素顺序取值为1,2,3,4,5,6,7,8,9。把该数组的行、列元素对调,构成一个新的二维数组。打印输出新、老数组的各个元素。,printf (“The new array:n“);for (j=0; j3; j+) for (k=0;
17、 k3; k+)printf (“%4d“, newjk);printf (“n“); ,.,程序中应说明两个3*3的二维数组。对调后的结果,存放在名为new的二维数组里。程序运行结果如图所示。,用一般数组元素初始化的方法,完成对字符数组的初始化。比如有说明语句:cher s7 = p, r, o, g, r, a, m; 或char b = C, h, i, n, a;,4.4 字符数组与字符串,4.4.1 字符数组与字符串,.,所谓“字符数组”,是指各元素是字符的数组。说明时,前的应该是char。比如说明语句:char name 15, t5; 说明了两个字符数组:数组name有15个元素
18、,它需要15个字节的存储空间;数组t有5个元素,它需要5个字节的存储空间。,.,字符数组初始化的方法,(1),(2),用字符串常量对字符数组初始化。比如:char s10= “Beijing“;,用花括号括住字符串常量对字符数组初始化。比如:char s10= “Beijing“;,(3),注意:用字符串常量或花括号括住字符串常量的办法对字符数组初始化时,所说明数组的必须比字符串拥有的字符个数大1,以便能在末尾安放字符串结束符“0”。,.,B,S0,e,S1,i,S2,j,S3,i,S4,n,S5,g,S6,0,S7,0,S8,0,S9,字符数组s在内存的存放,例:,程序实现,分析与讨论,(1
19、),(2),用字符串“Hi, morning!”对数组str元素赋初值,然后打印出该数组各个元素及所对应的ASCII码值。,#include “stdio.h“main()char str = “Hi, morning!“;int k = 0;while (strk != 0)printf (“%c = %dt“, strk, strk);if (k+1)%4 = 0)printf (“n“);k+;printf (“%c = %dn“, strk,strk); ,.,题目要求用字符串对数组初始化,因此数组的最后会有一个字节用于存放字符串结束符。所以,在用循环打印数组元素时,该循环将在遇见字符
20、串结束符时停止。,.,字符在内存中是以其ASCII码值的形式存放的。因此,让数组元素以“%c”格式打印时,就是打印出字符本身;以“%d”格式打印时,就打印出该字符对应的ASCII码值。,因空格和字符串结束符都不能直接打印出来,所以输出中有“=32”、“=0”的情况。 如图所示。,.,注意:scanf()接收到回车换行符或空格符时,都作为输入的结束,将它们以字符串结束符的形式存放。所以,通过scanf()往字符数组里存放字符串时,输入过程中不能有空格符。,从键盘上输入一个字符串(小于80个字符),存入数组中。求该字符串中所含字符的个数,即字符串的长度。注意,字符串的长度不包含字符串结束符。,4.
21、4.2 字符串的运算,.,C语言里只有字符串常量,没有字符串变量。这是由字符串的长度不能确定所致。,.,在C语言里,要实现字符串的运算,比如求字符串的长度、两个字符串的连接、字符串的复制等,都需通过字符数组进行。,.,在C语言的系统函数库里,提供了完成各种字符串运算的函数。只要将它们所在的头文件包含进程序,就可以直接调用它们。,例:,程序实现,分析与讨论,(1),(2),#include “stdio.h“main() char a80;int j = 0, n = 0;scanf (“%s“, a);while (a j+ != 0)n+;printf (“Length of %s = %d
22、n“, a, n); ,.,通过在scanf里使用格式符“%s”,达到从键盘接收字符串,并存入到字符数组中的目的。,.,.,注意:由于a是地址常量,所以不能在它的前面加上&。,从键盘上输入两个字符串,分别存入数组中。将第2个字符串连接到第1个字符串的后面,形成一个新的字符串,然后打印输出。,例:,程序实现,分析与讨论,(1),(2),#include “stdio.h“ main() char str180, str280;int j = 0, k = 0;printf (“Please enter first string:“);scanf (“%s“, str1);printf (“Ple
23、ase enter second string:“);scanf (“%s“, str2);while (str1j != 0)j+;do str1j+k = str2k;k+;while(str2k != 0);str1j+k = 0;printf (“The first string is %sn“, str1); ,.,为进行连接,须先找第1个字符串的结尾 (即字符串结束符所在位置)。从那开始,将另一个字符串内容复制过来。while循环就是顺着数组str1往后找字符串结束符所在的位置(由变量j记录)。,.,然后由dowhile循环,让str1从j+k开始顺序存放str2里的字符,直到遇见
24、字符串结束符。最后,不要忘记在str1的最后添加字符串结束符。,.,程序一次运行结果如图所示,4.4.3 常用的字符串处理函数,C语言向用户提供很多字符串操作函数。如字符串输入、输出函数,字符串的复制、连接、比较函数,求一个字符串长度的函数,等等。对这些函数,只需弄清在哪个头文件,以及正确的使用形式,然后把有关头文件包含到程序中即可。,.,字符串输入函数:gets(),1.,调用格式:gets ();函数功能:从键盘上接收一个字符串(以回车换行符为输入的结束标记),存入圆括号里由指定的字符数组。所在头文件:stdio.h。,字符串输出函数:puts(),2.,调用格式:puts ();函数功能
25、:输出圆括号里由指定的字符数组内容,并将所遇到的字符串结束标记转换成回车换行符输出。所在头文件:stdio.h。,.,注意:gets()和的scanf( ) 的区别是:scanf( )函数把“回车换行”或“空格”都看作是字符串输入的结束标记;gets()函数只把“回车换行”看作是字符串输入的结束标记。因此使用gets()来接收字符串时,“空格”也在接收之列。,调用格式:strcpy (, );函数功能:将中的字符串复制到由指定的字符数组中。复制时,是连同里的字符串结束符“0”一起复制的。可以是数组名,也可以是一个字符串常量。所在头文件:string.h。,字符串复制函数:strcpy(),3.
26、,.,注意:不能将一个字符串常量或字符型数组直接赋给另一个字符数组 !正确的做法是借助strcpy()函数,将字符串复制到数组中。,.,函数strcpy ()也可有第3个参数,这时调用格式是:strcpy (, , ); 功能是把指定的字符串的前个字符复制到由指定的数组里,然后加上一个“0”。,调用格式:strcat (, );函数功能:取消中字符串后的字符串结束符,然后把指定的数组中的字符串连接到它后面,在指定的数组里形成一个新的更长的字符串。所在头文件:string.h。,字符串连接函数:strcat(),4.,调用格式:int x; x = strcmp (, );函数功能:对和指定数组
27、中的字符自左至右逐个比较它们的ASCII码值,直到出现不同的字符或遇到“0”。若全部字符相同,则两个字符串相等,函数返回0 ;若出现不同字符,则第1数组中字符的ASCII码值大时,函数返回正数;小时函数返回负数。所在头文件:string.h。,字符串比较函数:strcmp(),5.,.,注意:不能用比较运算符“=”对两个字符串str1和str2进行比较,只能用strcmp()函数及其返回值,来判定它们之间的关系。,调用格式:int x; x = strlen ();函数功能:计算由所指定数组中字符串包含的字符个数。所在头文件:string.h。,字符串长度函数:strlen(),6.,.,注意
28、:该函数在统计字符串中的字符个数时,不包括字符串结束符。,调用格式:strlwr ();函数功能:将由所指数组中字符串里的大写字母全改为小写。所在头文件:string.h。,将字符串中大写字母改为小写字母函数:strlwr(),7.,编写检验密码程序,用户输入密码后,若正确,则显示信息:Now, you can do something!。若输入错误,则显示信息:Invalid password. Try again!,并控制至多重复3次。3次出错,给出信息:I am sorry, bye-bye!。,调用格式:strupr ();函数功能:将由所指数组中字符串里的小写字母全改为大写。所在头文
29、件:string.h。,将字符串中小写字母改为大写字母函数:strupr(),8.,例:,#include “stdio.h“ #include “string.h“ main() char str10;int k;for (k=0; k3;k+) printf (“Please enter your password:“);gets(str);if (strcmp (str, “913911“) if (k2)printf (“Invalid password. Try again!n“);,elseprintf (“Invalid password. “);elsebreak;if (k=
30、2)printf (“Now, you can do something!n“);elseprintf (“I am sorry, bye-bye!n“);getchar(); ,strupr (str2);printf (“The lenth of str1 = %dn“, ln);printf (“The small letter number = %dn “, low);printf (“The capital letter number = %dn“, up);printf (“str1=%sn str2=%sn“, str1,str2); ,例:,编写程序,接收用户输入的字符串,统计
31、字符串中字符的个数、小写字母的个数、大写字母的个数,并将小写改为大写。最后输出这些信息。,#include “stdio.h“ #include “string.h“ main() int k, ln, low = 0, up = 0;char str180, str280;printf (“Please enter a string:“);gets (str1);ln = strlen (str1);for (k=0; k=65 ,程序实现,分析与讨论,(1),(2),.,程序中由strlen()统计出字符串str1中的字符个数,控制for循环的次数,统计出str1里大、小写字母的个数;利用strcpy()把str1复制到str2,然后用strupr()把str2中的小写改为大写。,.,程序一次运行结果如图所示。,