晏文靖 yanwenjing@jscpu.com 第六单元 应用数组的程序设计 从现在开始,将详细讲述C语言的方方面面。第一章中的所有疑惑,都将一一消灭。 本章将讲述类型、变量、常量、数组等。这些概念的建立是进行进一步C语言学习的必要条件。同时,这些概念也是各种高级语言的共通概念。 晏文靖 yanwenjing@jscpu.com
内容提要 数组类型; 常用算法:查找、求最大最小值等; 用字符数组存取字符串; 使用字符串处理函数处理字符串 向函数传递一维数组和二维数组;
数组的用处 保存大量同类型的相关数据 如矩阵运算,表格数据等
现场编程 从键盘输入学生的成绩,求出成绩平均分并打印出所有学生的成绩。 打印出最高分的学生成绩。 打印出最高、最低分的学生成绩
a 数组(Array) 数组首地址 int score[10]; 定义一个有10个元素的数组,每个元素的类型均为int 使用score[0]、score[1]、score[2]、……、score[9]这样的形式访问每个元素。它们与普通变量没有任何区别 系统会在内存分配连续的10个int空间给此数组 数组下标可以是整型表达式 score[5] 数组首地址 score[4] score[3] score[1] a score[0]
数组的使用 数组的下标都是从0开始 对数组每个元素的使用与普通变量无异 可以用任意表达式作为下标,动态决定访问哪个元素 下标越界是大忌! for (i=0; i<SIZE; i++) a[i] = 2 * i; 下标越界是大忌! 使用大于最大下标的下标,将访问数组以外的空间。那里的数据是未知的,可能带来严重后果 sizeof可以用来获得数组大小
数组的定义与初始化 数组定义后的初值仍然是随机数,一般需要我们来初始化 int a[5] = { 12, 34, 56 ,78 ,9 }; 数组大小最好用符号常量来定义,以适应未来可能的变化 #define SIZE 10 int a[SIZE]; 数组大小定义好后,将永远不变
数组的特点 保存大量同类型的相关数据 快速地随机访问 一旦定义,不能改变大小
例 求Fibonacci数列前40项并输出
现场演示排序算法 排序的方法很多,主要有: 冒泡法 选择法 希尔法 插入法
应用举例(排序:冒泡法) 假定有5个无序的数 21,13,90,32,-1(从小到大) 第1轮比较4次:第1次 21 13 90 32 -1 进行交换 第2次 13 21 90 32 -1 不进行交换 第3次 13 21 90 32 -1 进行交换 第4次 13 21 32 90 -1 进行交换 第1轮比较结果: 13 21 32 -1 90 最大的数已排好
应用举例(排序:冒泡法)续 第2轮比较3次:第1次 13 21 32 -1 90 不进行交换 第2轮比较3次:第1次 13 21 32 -1 90 不进行交换 第2次 13 21 32 -1 90 不进行交换 第3次 13 21 32 -1 90 进行交换 第2轮比较结果: 13 21 -1 32 90 第3轮比较2次:第1次 13 21 -1 32 90 不进行交换 第2次 13 21 -1 32 90 进行交换 第3轮比较结果: 13 -1 21 32 90 第4轮比较1次:第1次 13 -1 21 32 90 进行交换 第4轮比较结果: -1 13 21 32 90
应用举例(排序:冒泡法)续 for(i=1;i<=N-1;i++) for(j=0;j<N-i;j++) if(a[j]>a[j+1]) {med=a[j]; a[j]=a[j+1]; a[j+1]=med;} 第j次比较 排序过程: (1)比较第一个数与第二个数,将小的调到前头;然后比较第二个数 与第三个数;依次类推,直至第n-1个数和第n个数比较为止— 第一趟冒泡排序,结果最大的数被安置在最后一个元素位置上 (2)对前n-1个数进行第二趟冒泡排序,结果使次大的数被安置在 第n-1个元素位置 (3)重复上述过程,共经过n-1趟冒泡排序后,排序结束
例 用简单选择法对10个数排序 排序过程: (1)首先通过n-1次比较,从n个数中找出最小的, 将它与第一个数 例 用简单选择法对10个数排序 排序过程: (1)首先通过n-1次比较,从n个数中找出最小的, 将它与第一个数 交换—第一趟选择排序,结果最小的数被安置在第一个元素位置上 (2)再通过n-2次比较,从剩余的n-1个数中找出关键字次小的记录, 将它与第二个数交换—第二趟选择排序 (3)重复上述过程,共经过n-1趟排序后,排序结束
min min min 例 i=1 初始: [ 49 38 65 97 76 13 27 ] 13 49 j j j j j j min min i=2 一趟: 13 [38 65 97 76 49 27 ] 27 38 j j j j j 二趟: 13 27 [65 97 76 49 38 ] 三趟: 13 27 38 [97 76 49 65 ] 四趟: 13 27 38 49 [76 97 65 ] 五趟: 13 27 38 49 65 [97 76 ] 六趟: 13 27 38 49 65 76 [97 ]
#include <stdio.h> main() { int a[10],i,j,min,x; printf("Input 10 numbers:\n"); for(i=0;i<10;i++) scanf("%d",&a[i]); printf("\n"); for(i=0;i<9;i++) { min=i; for(j=i+1;j<10;j++) if(a[j]<a[min]) min=j; if(i!=min) { x=a[i]; a[i]=a[min]; a[min]=x;} } printf("The sorted numbers:\n"); printf("%d ",a[i-1]);
二维数组的定义 格式: 数据类型 数组名[常量表达式][常量表达式] int b[2][3]; 格式: 数据类型 数组名[常量表达式][常量表达式] int b[2][3]; b为2×3(2行3列)的数组,注意下标范围 b[0] b[1] ---- b[0][0] b[0][1] b[0][2] b ---- b[1][0] b[1][1] b[1][2]
二维数组的存储结构 int b[2][3]; 存放顺序:按行存放,先顺序存放第一行的元素,再存放第二行的元素 b[0][2] b[0][1]
二维数组元素的引用 形式: 数组名[下标][下标] 二维数组元素的初始化 分行初始化: 形式: 数组名[下标][下标] 二维数组元素的初始化 分行初始化: 例 int a[2][3]={{1,2,3},{4,5,6}}; a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] 1 2 3 4 5 6 全部初始化 例 int a[2][3]={{1,2},{4}}; a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] 1 2 4 部分初始化 例 int a[][3]={{1},{4,5}}; a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] 1 4 5 第一维长度省略初始化 例 int a[2][3]={1,2,4}; a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] 1 2 4 部分初始化 按元素排列顺序初始化 例 int a[2][3]={1,2,3,4,5,6}; a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] 1 2 3 4 5 6 全部初始化 例 int a[][3]={1,2,3,4,5}; a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] 1 2 3 4 5 第一维长度省略初始化
二维数组的输入和输出 数组的输入和输出只能逐个对数组元素进行操作(字符数组例外) int b[2][3],i,j; 输出方法: 输入方法: scanf(“%d”,&a[i][j]); 输入整个数组元素: for (i=0;i<2;i++) for(j=0;j<3;j++) 输出方法: 输出第i行第j列元素: printf(“%d”,a[i][j]); 输出整个数组元素: for (i=0;i<2;i++) for(j=0;j<3;j++)
现场编程 二维数组中最大元素值及其行列号
例 计算总分和平均分 for (j=0; j<COURSE; j++) { sum[j] = 0; for (i=0; i<n; i++) sum[j] = sum[j] + score[i][j]; } aver[j] = (float) sum[j] / n;
字符串(String)与字符数组 字符串 字符数组 一串以'\0'结尾的字符在C语言中被看作字符串 每个元素都是字符类型的数组 char string[80];
字符数组的初始化 用字符型数据对数组进行初始化 用字符串常量直接对数组初始化 char str[6] = {'C','h','i','n','a','\0'}; 用字符串常量直接对数组初始化 char str[6] = { "China"}; char str[6] = "China"; char str[10] = "China"; char str[] = "China"; char str[] = {'C','h','i','n','a'};
字符数组的输入输出 逐个输入输出 一次性输入输出 char s[10]; scanf("%s",s); printf("%s",s); for (i=0; s[i]!='\0'; i++) { putchar(s[i]); } putchar('\n'); gets(s); puts(s);
scanf() int i; scanf("%d", &i); /* 假如不用&i,就用i,能达到效果吗?*/ char str[10]; scanf("%s", str); 不能读入带空格的字符串,gets()可以
gets puts 字符串的输入gets(字符数组) 字符串的输出 puts(字符数组) 格式:gets(ch) 功能:从终端输入一个字符串到字符数组ch中,输入回车键时结束,并将回车符‘\n’转换成 '\0' ,其中ch只能是一个字符数组名。 字符串的输出 puts(字符数组) 格式:puts(ch) 功能:向终端输出字符串,并将'\0'转换成'\n'输出,其中ch可以是某个字符数组名,也可以是一个字符串常量。
字符串处理函数(string.h) 字符串不能直接整体复制! 在<string.h>中定义了若干专门的字符串处理函数 strcpy: string copy char *strcpy(char *dest, const char *src); strlen: string length size_t strlen(const char *s); 返回字符串的实际长度,不包括'\0' strcat: string combination char *strcat(char *dest, const char *src); strcmp: string comparison int strcmp(const char *s1, const char *s2); 当出现第一对不相等的字符时,就由这两个字符决定所在字符串的大小 字符串不能直接整体复制! str1=str2 /*错误*/
‘\0’作为字符串结束符的天生缺陷 假若交给这些字符串处理函数的字符串没有'\0'会如何? '\0'很关键,如果没有,那么这些处理函数会一直进行处理直到遇到一个'\0'为止。此时可能已经把内存弄得乱七八糟 ANSI C定义了一些“n族”字符处理函数,包括strncpy、strncat、strncmp等,通过增加一个参数来限制处理的最大长度 char *strncpy(char *dest, const char *src, unsigned int count);
现场编程 输入一行文字,找出其中大写字母,小写字母,空格,数字及其它字符各有多少?
现场编程 从键盘任意输入5个学生的姓名,编程找出并输出按字典顺序排在最前面的学生姓名 等价于求最小字符串
#include <stdio.h> #include <string.h> #define ARRAY_SIZE 80 main() { int n, num; char str[ARRAY_SIZE], min[ARRAY_SIZE]; printf("Please enter five names:\n"); gets(str); strcpy(min, str); for (n=1; n<5; n++) if (strcmp(str, min) < 0) } printf("The min is:"); puts(min);
现场编程 将5个学生的姓名按字典顺序排列
二维字符数组 定义 引用 初始化 char str[3][6]; 相当于三个一维字符数组,可以存放三个字符串 可以单独输出某一个元素,也可以输出某一行的元素,即某一个字符串。如: printf(“%s”,str[2]); 初始化 char str[3][6]={”China”,”Japan”,”Korea”}; C h i n a \0 J a p a n \0 K o r e a \0