c语言gets函数存二维数组,第7章 数组 本章要求: 1、掌握C语言数组在内存中的存储形式 2、掌握一维数组和二维数组的定义及使用 3、掌握使用数组的方法...

Presentation on theme: "第7章 数组 本章要求: 1、掌握C语言数组在内存中的存储形式 2、掌握一维数组和二维数组的定义及使用 3、掌握使用数组的方法"— Presentation transcript:

第7章 数组 本章要求: 1、掌握C语言数组在内存中的存储形式 2、掌握一维数组和二维数组的定义及使用 3、掌握使用数组的方法

第7章 数组 本章要求: 1、掌握C语言数组在内存中的存储形式 2、掌握一维数组和二维数组的定义及使用 3、掌握使用数组的方法 4、掌握使用字符数组处理字符串数据的方法 5、掌握与数组有关的基本算法(如排序、查找、插入、删除等)的综合设计

第7章 数组 本章主要内容: 7.1 问题的提出与程序示例 7.2 一维数组的定义与引用 7.3 一维数组的程序举例 7.4 二维数组

第7章 数组 本章主要内容: 7.1 问题的提出与程序示例 7.2 一维数组的定义与引用 7.3 一维数组的程序举例 7.4 二维数组 7.5 字符数组与字符串 7.6 数组作为函数的参数 7.7 数组与字符串综合应用举例

第7章 数组 7.1 问题的提出与程序示例 数组是各种计算机程序设计语言中很重要的一个概念,用于处理大量数据的问题。为了处理方便,把具有相同类型的若干数据按有序的形式组织起来,这些按序排列的同类数据元素的集合称为数组。在C语言中,数组属于构造数据类型,一个数组元素可以是基本数据类型或是构造类型。

引例: 【例7-1】 编写程序输入一个班n(n<=100)个学生的“C 程序设计”课程的成绩,将这n个学生成绩从低到高输出,并输出它们的平均成绩。 分析:这个问题实际上是一个典型的排序问题,由于需要把100 个学生成绩从低到高排列,因此必须把这100 个学生成绩都保存下来,找出最低分,次最低分,…次最高分,最高分,按照前面所学的,必须要定义100个整型变量,“int x1,x2,x3……x100”,在C程序设计中,变量定义不能用省略号,只能写100个变量,如果要处理更多的学生成绩的话,利用前面的简单变量那就没法操作了。

引例: 如果不是100个数,而是1000,甚至是10000,更没办法解决了。 如果引入了数组,即使再多的数据量,用循环就可以解决问题。

下面是使用数组来实现的程序,通过选择法排序

#include "stdio.h" #define SIZE 100 void main ( ) { int i,k,temp,n,p; float sum=0; /*累加和单元置0*/ int grade[SIZE] ; printf("Enter n:"); scanf("%d",&n); printf("Please input %d students grade:",n); for (i=0;i

for (k=0;k

for (k=0;kgrade[i]) p=i; if(p!=k) {temp=grade[k];grade[k]=grade[p];grade[p]=temp; } } printf("After sorted:"); for(i=0;i

7.1 概 述 在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来。这些按序排列的同类数据元素的集合称为数组。

在C语言中, 数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。因此按数组元素的类型不同,数组又可分为: 数值数组、 字符数组、 指针数组、结构数组等各种类别。 按数组的维数可分为:一维数组、二维数组、多维数组。 数组在内存中占用一片连续的存储单元!!!,最低地址对应于数组的第一个元素,最高地址对应于最后一个元素,数组可以是一维的,也可以是多维的。

7.2 一维数组定义与引用 只有一个下标变量的数组,称为一维数组。 7.2.1 一维数组定义 一般形式为:

7.2 一维数组定义与引用 只有一个下标变量的数组,称为一维数组。 一维数组定义 一般形式为: 类型符 数组名 [数组长度],……; 其中,类型说明符是任一种基本数据类型或构造数据类型, 数组名是用户定义的数组标识符; 方括号中的常量表达式表示数据元素的个数,也称为数组的长度。 例如:int a[10]; 说明整型数组a,有10个元素。 float b[10],c[20]; 说明实型数组b,有10个元素,实型数组c,有20个元素。 char ch[20]; 说明字符数组ch,有20个元素。

7.2.1 一维数组的定义 说明: (1)用一个统一的数组名称来标识这一组数据; int age[3]; age

一维数组的定义 说明: (1)用一个统一的数组名称来标识这一组数据; int age[3]; age (2)用下标来指示数组中元素的序号。 例 age[0], age[1], age[2] (3)数组的类型实际上是指数组元素的取值类型。对于同一个数组,其所有元素的数据类型都是相同的。 (4)数组名的书写规则应符合标识符的书写规定,数组名不能与同一函数中其它变量名相同。例如: int a; float a[10]; 是错误的。

× 7.2.1 一维数组的定义 (6) 不能在方括号中用变量!!!来表示元素的个数, 但是可以是符号常数或常量表达式。例如:

一维数组的定义 (5)C语言中规定数组的下标从0开始,方括号中数组长度表示数组元素的个数。 例如:int a[5] 表示数组a有5个元素。因下标从0开始计算。因此5个元素分别为a[0],a[1],a[2],a[3],a[4]。 (6) 不能在方括号中用变量!!!来表示元素的个数, 但是可以是符号常数或常量表达式。例如: #define M 5 int a[3+2],b[7+M]; 是合法的。但是下述说明方式是错误的。 int n=5; int a[n]; ×

× (7)允许在同一个类型说明中,说明多个数组和多个变量。 void main( ) { int n; scanf(“%d”,&n)

int grade[n]; / *,不允许对数组的长度作动态定义*/ .. } × (7)允许在同一个类型说明中,说明多个数组和多个变量。 例如: int a,b,c,d,k1[10],k2[20]; (8)数组中各元素在内存占一片连续的存储空间,一维数组在内存中存放的顺序是下标大小的顺序。 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7]

一维数组元素的引用 数组元素是组成数组的基本单元,数组元素也是一种变量。引用数组元素有下标法和指针法。本章介绍下标法,指针法将在后面介绍。 使用下标法引用一维数组元素的一般形式为: 数组名[下标] 下标只能为整型常量或整型表达式。若为小数时,C编译将自动取整。

7.2.2 一维数组元素的引用 例: int a[8],b[6]; 表示a有8个元素,b有6个元素

一维数组元素的引用 例: int a[8],b[6]; 表示a有8个元素,b有6个元素 且元素从0开始编排:a[0],a[1]…,a [7], b[0],b[1]…,b [5] 例如,a[5],a[i+j],a[i++] 都是合法的数组元素。 a[1]=a[2]+b[1]+5; a[i]=b[i]; /* 将数组b的第i元素赋值给数组a的第i元素 */ b[i+1]=a[i+2]; /*将数组a的第i+2元素赋值给数组b的第i+1元素*/ a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] b[0] b[1] b[2] b[3] b[4] b[5]

数组元素的引用 注意: (1)数组元素的引用和数组定义在形式中有些相似,但这两者具有完全不同的含义。数组声明的方括号中给出的是某一维的长度,即表示元素的个数;而数组元素中的下标是该元素在数组中的位置标识。前者只能是常量,后者可以是常量,变量或表达式!!!! 例如,int a[10] 这里有a[10] 只是定义数组有10元素。而在程序中引用数组元素a[5]表示数组中的第6个元素。但是引用中不能用a[10],因为C语言中下标从0开始,数组a的最后一个元素是a[9]。

数组元素的引用 (2)C语言中对数组的引用不检验数组边界,即当引用时下标超界时(下标小于0或大于上界),C系统虽然不出错,但可能使其它变量的数组甚至程序代码被破坏,使得程序运行中断或输出错误的结果。 (3) 在C语言中只能通过下标变量使用数组元素,而不能一次引用整个数组。 例如,输出有10 个元素的数组必须使用循环语句逐个输出各下标变量: for(i=0; i<10; i++) printf("%d",a[i]); 而不能用一个语句输出整个数组,下面的写法是错误的: printf("%d",a); ×

7.2.3 一维数组的初始化 1、数组定义时初始化 类型符 数组名[长度]={值,值……值};

一维数组的初始化 1、数组定义时初始化 数组初始化赋值是指在数组说明时给数组元素赋予初值。 数组初始化是在编译阶段进行的。这样将减少运行时间,提高效率。 数组初始化的一般形式为: 类型符 数组名[长度]={值,值……值}; 其中: 在{ }中的各数据值即为各元素的初值,各值之间用逗号间隔。 例如: int a[10]={ 0,1,2,3,4,5,6,7,8,9 }; 相当于a[0]=0; a[1]=1;... a[9]=9;

7.2.3 一维数组的初始化 2、使用赋值语句初始化 用赋值语句初始化是在程序执行过程中实现的。例如: int a[3];

一维数组的初始化 2、使用赋值语句初始化 用赋值语句初始化是在程序执行过程中实现的。例如: int a[3]; a[0]=5;a[1]=8;a[2]=9; 对于数组的元素用赋值语句初始化,常常使用循环来完成,例如: int k,a[10]; …… for(k=0;k<10;k++) a[k]=1; /*对数组中所有元素赋初值为1*/

7.2.3 一维数组的初始化 C语言对数组的初始赋值的几点规定:

一维数组的初始化 C语言对数组的初始赋值的几点规定: (1)可以只给部分元素赋初值。当{ }中值的个数少于元素个数时,只给前面部分元素赋值。 例如: int a[10]={0,1,2,3,4}; 表示只给a[0]~a[4]5个元素赋值,而后5个元素自动赋0值。 (2)如果定义数组前加关键字static,表示是静态存储类型数组,对于数值类型数组,其元素初值系统自动全部赋值为0值。 (3)只能给元素逐个赋值,不能给数组整体赋值。 例如给十个元素全部赋1值,只能写为: int a[10]={1,1,1,1,1,1,1,1,1,1}; 而不能写为: int a[10]=1;×

一维数组的初始化 (4)如给全部元素赋值,则在数组说明中, 可以不给出数组元素的个数。例如: int a[5]={1,2,3,4,5}; 可写为: int a[]={1,2,3,4,5}; (5)当{ }中值的个数多于元素个数时, 系统出错。 例如: (6) 对于字符型一维数组,赋初值为‘\0’ char a[5]={‘*’}; 则相当于:char a[5]={‘*’,‘\0’,‘\0’,‘\0’,‘\0’}; × int a[5]={1,2,3,4,5,1 };

7.3一维数组程序举例 一维数组的一些基本操作 可通过循环给数组元素输入数据或输出数组元素 int a[10],i,;

for(i=0;i<10;i++) scanf(“%d”,&a[i]);

7.3一维数组程序举例 补充例子: 输入N个数据存入数组中,输出其中的最大元素及下标 #define N 10 void main()

{ int i,p,max,a[N]; printf("Enter %d Numbers\n",N); for(i=0;i max) { max = a[i]; p = i; } printf(" The Max Numbwer a[%d]=%d\n",p,max); }

7.3一维数组程序举例 一维数组的倒置 编程分析:将第0个元素与最后1个元素的交换、第1个元素与倒数第2个元素的交换、……、即第i个与第n-i-1个元素的交换,直到i

7.3一维数组程序举例 3.一维数组的倒置 补充例子:输入N个数据存入数组中,将其倒置存放,并打印输出。 #define N 10

main() { int i,t,a[N]; printf("Enter %d Numbers\n",N); /* 提示输入数据 */ for(i=0;i

7.3一维数组程序举例 【例7-2】从键盘上输人10个互不相同的数列到数组中,输出它们的最大值、最小值以及它们的下标,把最大值与最小值交换位置后再输出数列。 分析:定义二个变量max和min用于存放最大值和最小值,把第一个数就作为最大值和最小值的初始值赋给这二个变量,后面的数与这二个变量的值进行比较,如果比max变量中的值大,就用这个数替代max变量的值;如果比min变量中的值小,就用这个数替代min变量的值。同时记下每次发生替代的位置。通过n-1(设有n个数)的这样比较,max变量和min变量最后的值就是最大值和最小值。

7.3 一维数组的应用举例 #include #define N 10 void main( )

{ int i,j=0,k=0,max,min,data[N]; printf("input your data please!\n"); for(i=0;idata[i]) {min=data[i]; k=i;} }

7.3 一维数组的应用举例 printf("max:data[%d] = %d,min:data[%d] =%d\n",j,max,k,min); data[k]=max; /*交换最大值与最小值位置*/ data[j]=min; printf("after exchange data:\n"); for(i=0;i

7.3 一维数组的应用举例 【例7-3】分别利用迭代和数组方法,求出斐波那契数列的前20项数,按每行5个数输出。

分析:斐波那契数列的变化规律是:第一项和第二项都为1,从第三项开始,每一项的值是前二项之和,即。 迭代方法: #include #define N 20 void main( ) { int i,f1,f2,f3,n; f1=1;f2=1; /*第1项,第2项*/ printf("\n%d %d ",f1,f2);

7.3 一维数组的应用举例 n=2; for(i=1;i<=N-2;i++)

{ if(n%5==0) printf("\n"); /*输出每行5项后换行*/ f3=f2+f1; printf("%d ",f3); n++; f1=f2; f2=f3; /*关键的传递语句*/ } printf("\n");

7.3 一维数组的应用举例 数组方法: 与用迭代方法输出斐波那契数列相比,利用数组元素下标变化的连续性,编程更容易实现,直接利用公式就可以了。 #include void main( ) { int i, fib[20]={1,1}; /*数组初始化,置数组的前2个数为1,后18个数置0*/ for(i=2;i<20;i++) /*计算并存储其余18个数*/ fib[i]= fib[i-1]+ fib[i-2]; for(i=0;i<20;i++) printf("%-6d%c",fib[i],(i+1)%5==0)?’\n’:’’); /*利用条件表达式来决定输出换行符还是空格符*/ printf("\n"); }

7.3 一维数组的应用举例 数据的排序就是将一批数据由小大到(升序)或由大到小(降序)进行排列。常用的有选择法、冒泡法。 1.选择法排序 算法 (升序 ) ( 设有n个数,存放在数组A(1)…..A(n)中) 1)第1遍:从中选出最小的数,与第 1个数交换位置; 7 5 3 4 6 8 9 1

7 5 3 4 6 8 9 1 7.3 一维数组的应用举例 2)第2遍:除第1 个数外,其余n-1个数中选最小的数,与第2个数交换位置;

7.3 一维数组的应用举例 选择法排序算法的流程图:

7.3 一维数组的应用举例 选择法排序(升序)的C程序: for(i=0;ia[j]) p=j; if(i!=p) {s=a[i]; a[i]=a[p]; a[p]=s; } //交换两个元素 } for(i=0;i<10;i++) printf("%d",a[i]); 思考:如果按降序排,程序如何修改?

7.3 一维数组的应用举例 输入20个整数,用选择法由小到大排序,将其以每行10个数据打印输出。

#include #define N 20/*声明代表数据个数的符号常量*/ void main() { int i,j,p,t,a[N]; printf("input %d numbers:\n",N); for(i=0;ia[j]) p=j; t=a[i]; a[i]=a[p]; a[p]=t;/*交换a[i]和a[p]*/ } for(i=0;i

7.3 一维数组的应用举例 2.冒泡法排序(升序) 算法 :(将相邻两个数比较,大数交换到后面) 1)第 1 趟:将每相邻两个数比较,大数交换到后面,经n-1次两两相邻比较后,最大的数已交换到最后一个位置。 演示 2)第 2 趟:将前n-1个数(最大的数已在最后)按上法比较,经n-2次两两相邻比较后得次大的数; 3)依次类推,n个数共进行n-1趟比较, 在第j趟中要进行n-j次两两比较。

另一种排序法:冒泡排序法 思路:将相邻两个数比较,将小的调到前头. 共要进行5趟 这是第一趟过程

一维数组的应用举例:5.8.1 排序问题(教材P.166) 冒泡法排序算法的流程图:

7.3 一维数组的应用举例 冒泡排序法排序程序 #include

#define N 20/*声明代表数据个数的符号常量*/ void main() { int i,j,p,t,a[N]; printf("input %d numbers:\n",N); for(i=0;ia[j+1]) /*如果a[j]>a[j+1]*/ { t=a[j]; a[j]=a[j+1]; a[j+1]=t; } /*交换a[j]和a[j+1]*/ for(i=0;i

7.3 一维数组的应用举例 3、两种排序方法的比较 问题:两种排序方法那一种好? 结论:选择法排序比冒泡法排序算法优

7.4 二维数组 7.4.1 程序示例 【例7-4】求一个班50位学生数学,C程序,物理3门课的平均成绩并按矩阵形式输出。

7.4 二维数组 7.4.1 程序示例 【例7-4】求一个班50位学生数学,C程序,物理3门课的平均成绩并按矩阵形式输出。 分析:因为数据的排列是按矩阵排列,一行代表一个学生的记录,共有50条这样的记录,用一维数组只能存储一个学生的记录,而用二个下标的二维数组来存储和处理这些数据就非常方便。程序中使用了二维数组scores[M][N],二维数组有二个下标,行下标和列下标,行代表了学生数,列代表了学生的课程数。二维数组的输入、输出和其它的操作一般都要用二重循环来控制行下标和列下标的变化。再设一个一维数组aver[5]存放所求得每个学生的平均成绩,

【例7-4】一个学习小组有10个人,每个人有三门课的考试成绩。求每人的平均成绩。

#include #define M 10 #define N 3 void main( ) { int i,j; int scores[M][N]; /*定义二维数组存放M个同学N门课的成绩*/ float ave[M]; /*定义一维数组存放10个同学平均成绩*/ printf("请输入3个同学,3门课的成绩\n"); for(i=0;i

【例7-4】一个学习小组有10个人,每个人有三门课的考试成绩。求每人的平均成绩。

for(i=0;i

思考与讨论: 【例7-4】一个学习小组有10个人,每个人有三门课的考试成绩。求每人的平均成绩。

1)代表每个学生总成绩的变量ave[i]=0.0; 为什么要放到外循环内?如果将该语句移到外循环以外,程序输出结果如何? 2)如果不使用一维数组aver,只用一个二维数组来处理本问题,将每位学生的平均成绩存放在二维数组的第4列,即数组a的定义为“int a[5][4];”,上面的程序将如何修改?

7.4 二维数组 7.4.2 二维数组的定义 二维数组说明的一般形式是: 不能写成int a[3,4];

7.4 二维数组 7.4.2 二维数组的定义 二维数组说明的一般形式是: 类型符 数组名[常量表达式1][常量表达式2]…; 其中常量表达式1表示第一维下标的长度,常量表达式2 表示第二维下标的长度。 例如: int a[3][4]; 定义了一个3行4列的数组,数组名为a,其下标变量的类型为整型。该数组的下标变量共有3×4个,即: a[0][0], a[0][1], a[0][2], a[0][3] a[1][0], a[1][1], a[1][2], a[1][3] a[2][0], a[2][1], a[2][2], a[2][3] 不能写成int a[3,4];

7.4.2 二维数组的定义 从C语言二维数组的定义可以看出,一个二维数组也可以分解为多个一维数组。 可分解为三个一维数组,其数组名分别为a[0],a[1],a[2]。对这三个一维数组不需另作说明即可使用。这三个一维数组都有4个元素, 例如:一维数组a[0]的元素为 a[0][0],a[0][1],a[0][2],a[0][3]。 即: a[0][0], a[0][1], a[0][2], a[0][3] a[1][0], a[1][1], a[1][2], a[1][3] a[2][0], a[2][1], a[2][2], a[2][3]

7.4.2 二维数组的定义 注意:二维数组是按行排列的。即放完一行之后顺次放入第二行。2维数组在内在中占一片连续存储空间,二维数组a在内在的映像如右图所示

7.4.3 二维数组元素的引用 二维数组的元素的引用形式为: 数组名[下标][下标] 其中下标应为整型常量或整型表达式。

二维数组元素的引用 二维数组的元素的引用形式为: 数组名[下标][下标] 其中下标应为整型常量或整型表达式。 例如:a[3][4]表示a数组 行 列的元素。 注意: 下标元素与数组定义的区别 例如: int a[3][4]; a[2][3]=10; a[1][2]=2*a[2][3]

7.4 二维数组 二维数组的初始化 二维数组初始化也是在类型说明时给各下标变量赋以初值。 二维数组可按行分段赋值,也可按行连续赋值。 1. 按行分段赋值可写为 int a[5][3]={{80,75,92},{61,65,71},{59,63,70}, {85,87,90},{76,77,85}}; 2. 按行连续赋值可写为 int a[5][3]={ 80,75,92,61,65,71,59,63, 70,85,87, 90,76,77,85 }; 这两种赋初值的结果是完全相同的。

7.4 二维数组 二维数组初始化赋值说明: 1.可以只对部分元素赋初值,未赋初值的元素自动取0值。 1 0 0 2 0 0

7.4 二维数组 二维数组初始化赋值说明: 1.可以只对部分元素赋初值,未赋初值的元素自动取0值。 例如: int a[3][3]={{1},{2},{3}}; 是对每一行的第一列元素赋值,未赋值的元素取0值。 而: int a [3][3]={{1},{0,2,0},{0,0,3}}; 赋值后的元素如右图 2. 如对全部元素赋初值,则第一维的长度可以不给出。 例如: int a[3][3]={1,2,3,4,5,6,7,8,9}; 可以写为: int a[][3]={1,2,3,4,5,6,7,8,9};

补充: 二维数组的基本操作 二维数组的操作一般需要使用二重循环。 1.给二维数组a输入数据 设所有变量及数组己定义,其程序段如下:

for(i=0;i

补充: 二维数组的基本操作 补充 输出下面二维数组中的最大元素及其下标。 main()

补充 输出下面二维数组中的最大元素及其下标。 main() { int i,j,row=0,colum=0,max; int a[3][4]={{12,23,3,5},{45,32,56,6}, {9,16,34,21}}; max=a[0][0]; for(i=0;i<3;i++) for(j=0;j<4;j++) if (a[i][j]>max) { max=a[i][j];row=i;colum=j;} printf("max=%d,row=%d,colum=%d\n",max,row,colum); }

7.4.5 程序举例 【例7-5】将矩阵转置存放,并打印输出

程序举例 【例7-5】将矩阵转置存放,并打印输出 如果是方阵,即a是N*N的二维数组,则可以不必定义另一数组,否则就需要再定义新数组。方阵的转置以对角线为基准,对应元素交换,下面2段的程序代码都能实现方阵的转置。 for(i=1; i

7.4.5 程序举例 【例7-5】将下面矩阵的转置存放,并打印输出 #define N 3 void main() { int i,j,t;

程序举例 【例7-5】将下面矩阵的转置存放,并打印输出 #define N 3 void main() { int i,j,t; int a[N][N]={{1,4,7},{2,5,8},{3,6,9}}; for(i=0; i

7.4.5 程序举例 如果不是方阵,则要定义另一个数组。设a是M*N的矩阵,要重新定义一个N*M的二级数组b

程序举例 如果不是方阵,则要定义另一个数组。设a是M*N的矩阵,要重新定义一个N*M的二级数组b 算法: b[j][i] = a[i][j] for(i=0; i

程序举例 补充 设某一个班共有30个学生,期末考试5门课程,请编一程序评定学生的奖学金,要求打印输出一、二等奖学金学生的学号和各门课成绩。(奖学金评定标准是:总成绩超过全班总平成绩20%发给一等奖学金,超过全班总平成绩10%发给二等奖学金。) 编程分析:本题定义一个二维数组x来存放学生的各科目成绩和他的总成绩(用后1列),为便于程序维护,分别定义存放学生人数和课程数目的NUM 和KCN符号常量。

#include

#define NUM /* 学生人数的符号常量 */ #define KCN /* 课程数目的符号常量 */ void main() { int i, j, k; float x[NUM][KCN+1]; /*存放学生成绩,第最后1列为该学生的总成绩 */ float sum, tt=0.0, ver; for(i=0;i

思考与讨论: for(i=0;i

if( x[i][KCN+1]>=1.2*ver) { printf("%5d",i+1); for(j=0;j=1.1*ver&& x[i][KCN+1]<1.2*ver) printf("grade B\n"); } } 思考与讨论: 1)上面程序中,定义了存放学生人数和课程数目的符号常量,使得程序便于维护,如处理的班级学生人数不是30,而是50,考试课目不是5门,而是6门,则只需修改宏定义即可。 2)程序中代表全班总成绩的变量tt和代表每位学生成绩的总分变量sum能否都放在定义时赋初值?为什么?

7.4.5 程序举例 【例7-6】编写程序,打印出以下形式的杨辉三角形。 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1

程序举例 【例7-6】编写程序,打印出以下形式的杨辉三角形。 1 1 1 分析:可以将杨辉三角形的值放在一个正方形矩阵的下半三角中.先考虑第0列和对角线都置成1,其它的位置的数:前面一行的前一列+前面一行的同一列。 打印时要控制好行列的变化,只打印下三角。

#include

#define N 7 void put_data(int [N][N],int );/*函数原型说明*/ void print_yhsj(int [N][N],int ); /*函数原型说明*/ void main( ) { int yhsj[N][N]; put_data(yhsj,7); /*按规律给数组元素赋值*/ print_yhsj(yhsj,7);/输出杨辉三角形*/ } void put_data(int yh[N][N],int n) { int i,j; for(i=0;i

for(i=2;i

for(j=1;j

7.5 字符数组与字符串 7.5.1 字符串与字符数组的关系 字符串是由若干个有效字符构成且以‘\0’作为结束标志的一个字符序列。用一对双引号括起来的一串字符,如“C Language”。 字符串的存储是通过字符数组实现,即将一个个的字符存入到字符数组中,而且作为字符串结束字符'\0'也存入到字符数组中,也占用一个字节的存储单元,但是不计入字符串的实际长度。每个字符串在内存中都占用一片连续的存储空间,具有唯一确定的首地址。如果将字符串存储到一个一维的字符数组中,那么这个字符数组的名字代表了这个字符串的首地址。实际上,在C语言中,字符串常量被隐含处理成一个以'\0'结尾的字符型一维数组。

7.5 字符数组与字符串 7.5.1 字符串与字符数组的关系 字符数组与字符串的区别是:字符数组的每个元素中可存放一个字符,但不限定最后一个字符是什么。如果在字符数组中的有效字符后面加上'\0'这一特定情况下,可以把这个一维字符型数组“当成”字符串变量,可以说字符串是字符数组的一种具体应用。

7.5.2 字符数组与字符串 在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。

字符串总是以‘\0’作为串的结束符。因此当把一个字符串存入一个数组时, 也把结束符‘\0’存入数组,并以此作为该字符串是否结束的标志。 有了‘\0’标志后,就不必再用字符数组的长度来判断字符串的长度了。 实际上,字符串就是一种字符型数组,并且这个数组的最后一个单元是一个字符结束标志“\0”,也就是说字符串是一种以“\0”结尾的字符数组。

7.5.2 字符串与字符数组 一、字符数组的定义 二、字符数组的初始化 字符数组类型说明的形式与前面介绍的数值数组相同。

例如: char c[10]; 定义有10个元素的数组 例如: char ch[5][10]; 即为5*10 的二维字符数组。 二、字符数组的初始化 字符数组也可在定义时作初始化赋值。 (1)逐个元素初始化,当初始化数据少于数组长度,多余元素为“空”('\0'),当初始化数据多于元素个数时,将出错。 例如: char c[10]={'c',' ','p','r','o','g','r','a','m'}; 赋值后各元素的值为:其中c[9]未赋值,由系统自动赋予为空字符‘\0’ 值。只要所赋初值的字符个数少于数组元素的个数时,系统自动在其后的元素中加入’\0’。

C p r o g r a m \0 2、字符数组可以用字符串来初始化,例如 C语言允许用字符串的方式对数组作初始化赋值。 例如:

char c[]={'C', ' ','p','r','o','g','r','a','m ', ' \0',}; 可写为: char c[]={"C program"}; 或去掉{}写为: char c[]="C program"; 字符在内存存放情况: C p r o g r a m \0

2、字符串在存储时,系统自动在其后加上结束标志'\0'(占1字节,其值为二进制0)。但字符数组并不要求其最后一个元素是'\0',例如要注意下面数组使用的区别:

char c1[5]={'G','o','o','d','!'}; char c2[ ]={“Good!”};也可以用char c2[ ]= “Good!”; 上面的定义不等价,字符数组c1不能当字符串使用,因为其最后一个元素不是结束标志。

7.5.2 字符数组与初值化 注意:不可以用赋值语句给字符数组整体赋一串字符。 例如:char str[10];

str="Student"; //错误 因为数组名str是一个地址常量,不能重新赋值。 这里的赋值语句与前面的初始化语句是不同的,不能混为一谈。 例如: char str1[10]= “Student”,str2[10]; //正确,这里是定义字符数组并初始化*/ str2=str1; × /*错误,赋值不合法*/。

字符数组的举例 【例7-7】输入一行包括字母、空格、数字的一串字符序列(用回车+ctrl z结束),编写程序,把这一行中的数字字符转换成一个整数。 例如:输入3a5ss6ww8 s 1 (代表回车键) 输出结果:35681 分析:程序中循环输入一个字符,判断是否是数字字符,是的话,把它转换成数字,然后通过乘10加个位数的方法把数字序列转换成整数。 如何判断?

7.5.2 字符数组的举例 #include void main() { int convert=0,d;

字符数组的举例 #include void main() { int convert=0,d; char cin; printf("please input a series of letter and digit\n"); while((cin=getchar())!=EOF) if(cin>='0'&&cin<='9') /*判断是否是数字字符*/ { d=cin-'0'; /*把数字字符转换成数字*/ convert=convert*10+d; /*乘10+转换后的一位数,使得数字序列变成整数*/ } printf("transfered result=%d\n",convert);

7.5.3 字符数组的输入输出 字符数组的输入输出一般采用下面两种方法:

字符数组的输入输出 字符数组的输入输出一般采用下面两种方法: 可用printf函数和scanf函数中使用“%s”格式控制符一次性输出\输入一个字符数组中的字符串。 1、用“%c”格式符逐个输入输出。 2、用“%s”格式符按字符串输入输出。 例如,有定义:char c[6]; 可使用如下语句来输入输出。 scanf("%s",c); printf("%s",c);

7.5.3 字符数组的输入输出 说明: (1)输出时,遇'\0'结束,且输出字符中不包含'\0'。

(2)“%s”格式输出字符串时,printf()函数的输出项是字符数组名,而不是元素名。 char c[] = "Good!"; printf("%s",c); printf("%c",c[0]); printf("%s",c[0]); /* 错误 */ (3)“%s”格式输出时,即使数组长度大于字符串长度,遇'\0'也结束。 例如:char c[10] = {"Good!"}; printf("%s",c); /*只输出5个字符 */

7.5.3 字符数组的输入输出 说明: (4)“%s”格式输出时,若数组中包含一个以上'\0',遇第一个'\0'时结束。

例如:char c[] = {"Good!\0boy"}; printf("%s",c); 只输出结果是:Good! (5)输入时,遇回车键结束,但获得的字符中不包含回车键本身,而是在字符串末尾添'\0'。因此,定义的字符数组必须有足够的长度,以容纳所输入的字符。(如,输入5个字符,定义的字符数组至少应有6个元素)。

7.5.3 字符数组的输入输出 说明: (6)一个scanf函数输入多个字符串,输入时以“空格”键作为字符串间的分隔。

例如:char str1[5],str2[5],str3[5]; scanf("%s%s%s",str1,str2,str3); 输入数据:How are you? str1、str2、str3获得的数据如图4-8所示。

7.5.3 字符数组的输入输出 说明: (7) C语言中,数组名代表该数组的起始地址,因此,scanf()函数中不需要地址运算符&。

char str[13]; scanf("%s",str); scanf("%s",&str); /* 此语句是错误的 */

7.5.3 字符数组的输入输出 3、用gets( )或puts( )输入或输出一个字符串 输入:gets(str);

其中str用来保存输入字符串的起始地址,可以是字符数组名,字符数组元素的地址或下一章将介绍的字符指针。gets函数用来从键盘读入一个字符串(包括空格符),直到读入一个换行符为止。换行符读入后,不作为字符串的内容,系统自动用'\0'代替。注意:gets函数并不以空格作为字符串输入结束的标志,而只以回车作为输入结束。这是与scanf函数不同的。

7.5.3 字符数组的输入输出 str为数组名或指针变量 输出:puts( str)

功能:从括号内的参数给出的地址开始,依次输出存储单元中的字符,当遇到第一个‘\0’时输出结束,且自动输出一个换行符。 等价于: printf("%s\n",str); 因为gets( )或puts( )是C语言提供的标准输入/输出函数库中的函数,在使用时,要在程序的开头加上文件包含的编译预处理命令。

7.5.4 字符串处理函数 1.字符串连接函数strcat 格式: strcat (str1,str2)

例 字符串的连接示例 #include "string.h“ #include"stdio.h" void main() { char st1[30]="My name is "; char st2[10]; printf("input your name:\n"); gets(st2); strcat(st1,st2); puts(st1); }

7.5.3 字符串处理函数 2.字符串拷贝函数strcpy 格式: strcpy (str1,str2)

例: #include"string.h" main() { char st1[15],st2[]="C Language"; strcpy(st1,st2); puts(st1);printf("\n"); } str1=str2;× Str1=“student” × 只能用字符串复制

7.5.3 字符串处理函数 3.字符串比较函数strcmp 格式: strcmp(str1,str2)

功能:按照ASCII码顺序比较两个数组中的字符串,并由函数返回值返回比较结果。 str1=str2, 返回值=0; str1>str2, 返回值>0; str1

7.5.3 字符串处理函数 例 字符串比较操作示例 #include "string.h" void main() { int k;

char st1[15],st2[]="C Language"; printf("input a string:\n"); gets(st1); k=strcmp(st1,st2); if(k==0) printf("st1=st2\n"); if(k>0) printf("st1>st2\n"); if(k<0) printf("st1

7.5.3 字符串处理函数 4. 测字符串长度函数strlen 例: 格式: strlen(str)

功能:测字符串的实际长度(不含字符串结束标志‘\0’) 并作为函数返回值。 例: #include"string.h" main() { int k; static char st[]="C language"; k=strlen(st); printf("The lenth of the string is %d\n",k); }

7.6 数组作为函数的参数 数组作为函数参数主要分二种情况,一种是数组元素作为函数参数,这种情况与普通变量作为实参一样,是将数组元素的值传给形参。形参的变化不会影响实参数组元素,这种参数传递形式为值传递,前面的章节已经讨论,不再叙述。另一种是把数组名作为实参,要求函数的形参是相同类型的数组或指针(第8章涉及),这种方式是把实参数组的起始地址传递给形参数组,这种传递方式为地址传递。

7.6 数组作为函数的参数 7.6.1数组名作为函数参数 因为数组名是个地址常量,所以数组名作为函数的实参,传递的是地址,即把实参数组的首地址传递给形参。对应的形参可以是相同类型的同维数组(或将学到的指针)。调用函数和被调用函数存取的是相同的存储空间,函数中对形参数组中的数组元素任何改变也就是对实参数组的数组元素的改变。 【例7-8】编写函数my_strcat(char s[ ],char t[ ]),将存储在字符数组t的字符串连接到s数组的字符串后面。 分析:利用每个字符串的结束字符‘\0’,找到第一个字符串的结束位置;然后把第二个字符串的第一个字符存储到第一个字符串的结束位置,覆盖‘\0’,接着依次放在第一串的后面位置。当然定义第一个字符串时要留出足够的空间。

#include

void my_strcat (char s[],char t[])/*函数定义,形参为字符数组s和t*/ { int i=0,j=0; while(s[i]!=‘\0’) i++; /*找到第一个字符串结束的位置*/ while(t[j]!='\0') /*将到第二个字符串连接到第一个字符串的后面,直到遇到第二串的结束标志*/ { s[i]=t[j]; i++; j++; } s[i]='\0'; /*在第一个字符串的后面加上结束符标志*/

{ char str1[35]="Welcome Ningbo "; char str2[]="University!";

void main() { char str1[35]="Welcome Ningbo "; char str2[]="University!"; my_strcat (str1,str2); /*调用函数,实参是二个已赋字符串的字符数组,注意不能写成my_strcat (str1[ ], str2[ ])*/ puts(str1); /*输出连接后的字符串*/ } 1000 str1 1004 2000H str2 t 2000 2004 1000H s W e l c ……… U n i o v 地址值

7.6.2 字符与字符串程序举例 【例7-9】从字符串s中删除指定的字符c #include void del_letter(char s[],char c) { int i,k=0; for(i=0;s[i]!='\0';i++) if(s[i]!=c) s[k++]=s[i];/*当遇到要删除的字符时,下标i仍然递增,但下标k不变化*/ s[k]='\0'; }

7.6.2 字符与字符串程序举例 void main() { char c; char str[20]; printf("please a series of character:"); gets(str); printf("please input a character to be delete:"); scanf("%c",&c); del_letter(str,c);/*调用函数,实参是一个指定的字符数组和指定的字符*/ printf(" str=%s\n",str);/*输出删除字符后的字符串*/ }

7.6.2 字符与字符串程序举例 【例7-10】编写函数my_strcopy(char s[ ],char t[ ]),将存储在字符数组t的字符串复制到s数组的存储空间中。 复制字符串就是将源字符串中的字符包括‘\0’逐个赋值到指定的存储单元中。可用这样的表达式(s[i]=t[i])!= ‘\0’,先将t[i]赋值给s[i],然后再判别s[i]的值是否等于‘\0’,若等于‘\0’则表示字符串复制结束。 #include void my_strcopy (char s[ ],char t[ ]) /*复制,形参是二个字符数组*/ { int i=0; while((s[i]=t[i])!='\0') i++; /*将t[i]赋值给s[i],直到s[i]的值等于'\0'*/ }

void main( ) { char str1[]="where is Ningbo university?",str2[ ]= "Welcome Ningbo"; printf("we are doing copy to str1 from str \n"); printf("Before copy str1=%s\n",str1); printf("Before copy str2=%s\n",str2); my_strcopy (str1, str2);/*调用函数*/ printf("After copy to str1 from str2\n"); printf("str2=%s\n",str2); printf("str1=%s\n",str1); }

【例7-11】编写函数my_strcmp(char s[ ],char t[ ]),比较二个串的大小。即实现库函数strcmp的功能。

分析:函数中,从两个字符串的初始位置开始,将它们对应位置上的字符进行比较,只要当前字符相等,并且不是两串同时到尾,接着去比较下一个字符。 当s[i]或t[i]的值为'\0',表示其中一个串已经到串尾,表达式s[i]==t[i]的值为“假”,退出循环;若两串完全相等,同时到串尾,表达式s[i]==t[i]的值为“真”,但是s[i]或t[i]的值为'\0',也结束循环,这里判断条件“ s[i]==t[i] && s[i]”也可以改为“ s[i]==t[i] && t[i]”

#include

my_strcmp (char s[ ],char t[ ]) { int i=0; while(s[i]==t[i] && s[i]) i++; /*二种情况退出循环:出现了不相等字符;字符两两相等,但遇到了串结束符*/ return(s[i]-t[i]); } /*返回两个字符相减而得到的函数值*/ void main( ) { char str1[ ]= "student",str2[20]; printf("str1=%s\n",str1); printf("please input str2="); gets(str2); if( my_strcmp(str1,str2)>0) printf("str1>str2\n"); else if ( my_strcmp(str1,str2)<0) printf("str1

7.7 数组与字符串综合应用举例 在程序设计过程中经常需要处理成批的相关数据,数组是解决此类问题的最佳工具。本节介绍数据排序、查找、插入、字符串处理等与数组有关的常用算法,通过这些问题的处理使读者掌握数组的基本使用方法。 7.7.1 数据颠倒存放问题 【例7-12】借助一个存储单元,将数组中的数按颠倒的顺序重新存放并输出。 将变量i的初值定位在数组data的第一个位置,变量j的初值定位在最后位置,当i=j为止。 i 直到i>=j j

#include

#define LONG 10 void invert_data(int [ ],int); /*函数原型说明*/ void print_data(int [ ],int); /*函数原型说明*/ void main( ) { int data[LONG],i; printf("从键盘输入10个原始数据\n"); for(i=0;i

void print_data(int s[ ],int n )

{ int i; for(i=0;i=j时对调完成*/ { temp=s[i]; s[i]=s[j]; s[j]=temp; }

7.7.2 排序问题(已讲) 1.冒泡法排序 冒泡法的思路是:通过对相邻两个数之间的比较和交换,小的数交换到前面,大的数交换到后面,使得较大的数逐渐从顶部沉到底部,这称为一趟,经过n-1趟的比较和交换,大数沉到“水底”(即最后),最小数冒泡升起到上面(即最前)。 算法如下: (1)设n个数存放在数组a[0]~a[n-1]中,对这n个数每相邻两个数比较,小的交换到前面,经过n-1次两两相邻比较后,最大的数已放在最后一个位置(沉底)。 (2)第二趟对余下的n-1个数按上述方法两两比较,经过n-2次的两两相邻的比较后,次大的数已放在倒数第二个位置。 (3)依次类推,共进行n-1趟的比较,所有的数都排序完毕。 为了进一步理解“冒泡排序法“的基本思路,下面以5个数从小到大的顺序为例具体说明第一耥数据交换的情况,如图7-6所示,大数90已“沉”到了最底下,这完成了第一耥。

7-6 第一耥数据交换情况 90 12 23 45 10

【例7-13】利用冒泡法对10个数据进行排序 源程序 文件名:exp7_13.cpp #include #define N 10 void main() { int a[N]; int i,j,t; printf("input 10 number:\n"); for(i=0;i

for(i=0;i

for(j=0;j<=N-i-1;j++)/*内循环控制对相邻的两个数进行两两比较,范围是0~N-i*/ if(a[j]>a[j+1]) {t=a[j];a[j]=a[j+1];a[j+1]=t;} /*如果大数在前,小数在后,则进行交换*/ printf("the sorted numbers:\n"); for(i=0;i<10;i++) /*输出排序后的数据序列*/ printf("%d ",a[i]); printf("\n"); }

7.7.3 查找问题 查找问题分二类,对于无序的一批数据中,查找只能用顺序法,即一个个的找;而对于有序的数据查则可以用顺序法或折半法。

假定存放在s数组中的n个数由小到大排列,从键盘上输入一个数,判断该数是否在数组中,若在,输出该数在数组中的位置并删除这个数后输出;若没找到,输出没在的信息,用顺序查找和折半查找。 (1)顺序查找(略,自学) 【例7-14(1)】顺序查找的方法是,把要查找的数逐个与数组的第一个元素直到最后一个元素比较,若相等,则找到程序结束,如果一直都没找到,说明要找的数不在数组中,输出没找到的信息。 思考:如果一批数据中有多个要查找的数据,要求将所有数据都查找出来,上面的程序应作如何修改?

7.7.3 查找问题 2.折半查找法(只能对有序数列进行查找) 【例7-14(2)】使用折半查找法,在一批有序数据数列中查找给定的数x.

编程分析:设n个有序数(从小到大)存放在数组a[0]----a[n-1]中,要查找的数为x。用变量bot、top、mid 分别表示查找数据范围的底部(数组下界)、顶部(数组的上界)和中间,mid=(top+bot)/2。 折半查找的算法如下: (1)x=a[mid],则已找到退出循环,否则进行下面的判断; (2)xa[mid],x必定落在mid+1和top的范围之内,即bot=mid+1; (4)在确定了新的查找范围后,重复进行以上比较,直到找到或者bot<=top。 可见二分查找每进行一次,查找范围就缩小一半

#include

#define N 10 void print_del(int [ ],int); void main( ) { int s[N]={3,5,9,10,20,23,45,56,66,70},low=0,high=N,mid,x; printf("Input a number to search:"); scanf("%d",&x); while (low<=high) { mid=(low+high)/2; /*取中间位置*/ if(x==s[mid]) { printf("The position in array is %d\n",mid+1); print_del(s,mid); break; } else if(x

else /*查找范围在数组的后半部分*/ low=mid+1;

找到的运行结果示意 else /*查找范围在数组的后半部分*/ low=mid+1; if(low>high) printf("%d not be found\n",x); } void print_del(int s[],int n) { int i; for(i=n;i

7.7.4 插入数据法 【例7-15】把一个给定的数据x按大小顺序插入已排好序的数组中,插入后数组元素仍然有序。

编程分析:设n个有序数据(从小到大)存放在数组a[0]—a[n-1]中,要插入的数x。首先确定x插在数组中的位置p。 (1)首先确定x插在数组中的位置p,实现的语句如下。 while(x>a[p]&&p= p; i--) a[i]=a[i-1], a[p]=x; 实现插入的完整程序如下(数组方法):

#define N 10 /* N代表数据的个数 */

void main() { int a[N+1]={1,4,7,13,16,19,28,36,49,60}; /* 定义N+1个元素的数组 */ int x,p,i; printf(" Befor Inserted:\n"); for(i=0;i

p=0; while(x>a[p]&&pp; i--) /* 将元素往后移,空出x所在的位置 */ a[i]=a[i-1]; a[p]=x; /* 将x插入到数组中 */ printf(" After Inserted:\n"); for(i=0;i<=N;i++) /* 输出插入后的数据序列 */ printf(" %d ",a[i]); }

7.7.5 字符串的处理 1.字符过滤 字符过滤问题一般指的是指定或输入一个字符串,然后过滤一些特定的字符或字符数字。通常做法是从字符串的头开始逐个检查每个字符是否就是要过滤的字符?如果是的,用后面的字符覆盖它;否则再检查下一个字符,直到一个字符串的结束。 【例7-16】输入一个字符串,过滤此串,只保留串中的字母字符,并统计新生成串中包含的字母个数。 例如:输入的字符串为Abab1234*&$df4,新生成的串为ABabdf 。 这里是要把字符串中的大小写字母筛选出来。首先把字符串放到一维字符数组中,从数组的初始位置开始,对每个字符都进行判断是否是大小写的字母,判断的条件用ptr[i]>=’a’&& ptr[i]<=’z’||ptr[i]>=’A’&& ptr[i]<=’Z’,直到遇到字符串的结束标志’\0’。

#include

#define N 80 void main() { char str[N]; int k; printf("input a string:"); gets(str); /*输入一个原始串*/ printf("The original string is :"); puts(str); k=fun(str); /*调用函数,过滤特定字符*/ printf("The new string is :"); printf("There are %d char in the new string.",k); }

fun(char ptr[ ]) { int i,j; for(i=0,j=0; ptr[i)!=’\0’;i++) /*把大小写字母筛选出来*/ if(ptr[i]>=’a’&& ptr[i]<=’z’||ptr[i]>=’A’&& ptr[i]<=’Z’) {ptr[j]= ptr[i]; j++;} ptr[j]=’\0’; return j; }

例7-18】编写函数统计输入文本中单词的个数,单词之间用空格符、换行符、制表符隔开。

分析:这里用一个状态变量state的值来标志当前读到的字符是在单词的内部还是在外部。如果读入的字符是分隔符,则state置flag_OUT,表示已在一个单词的外部;如果读入的字符不是分隔符且state为flag_out,则表示遇到了单词的第一个字符,置状态变量标志state为flag_IN,单词个数增1。接着再读入下一个字符,若state为flag_IN,同时又没读到分隔符,说明当前读到的字符仍然是这个单词的一部分,则不做任何操作。

#include

#define FLAG_IN 1 /*在一个单词的内部标志*/ #define FLAG_OUT 0 /*在一个单词的外部标志*/ int countword() ; void main() {int n; n=countword(); printf("n=%d\n",n); }

int countword() { int c,nw,state; state=FLAG_OUT; nw=0; while((c=getchar())!=EOF) { if(c==' '||c=='\n'|| c=='\t') state=FLAG_OUT ; /*在一个单词的外部*/ else if(state==FLAG_OUT) /*如果遇到单词的第一个字符*/ { state=FLAG_IN; /*状态置成单词的内部*/ nw++; /*单词个数增加*/ } return nw; }

本章小结 本章介绍了一维、二维数组的定义及应用、数组、字符串等内容。

数组的声明由类型符、数组名、数组长度(数组元素个数)三部分组成。数组元素又称为下标变量。数组的类型是指下标变量取值的类型。 对数组的赋值可以用数组初始化赋值,输入函数动态赋值和赋值语句赋值三种方法实现。对数值数组不能用赋值语句整体赋值、输入或输出,而必须用循环语句逐个对数组元素进行操作。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值