第五章 数组、字符串、指针 荆蕾 烟台大学文经学院
第5章 数组、字符串、指针 5.1 数组概述 5.2 一维数组 5.3 二维数组与多维数组 5.5 数组与指针 5.5 字符数组与字符串 第5章 数组、字符串、指针 5.1 数组概述 5.2 一维数组 5.3 二维数组与多维数组 5.5 数组与指针 5.5 字符数组与字符串 5.6 字符串与字符指针 5.7 指针数组与多级指针变量 5.8 应用程序举例 数组是各种计算机程序设计语言中很重要的一个概念,用于处理大量数据的问题。为了处理方便,把具有相同类型的若干数据按有序的形式组织起来,这些按序排列的同类数据元素的集合称为数组。在C语言中,数组属于构造数据类型,一个数组元素可以是基本数据类型或是构造类型。
什么是数组? 是一种数据类型,属于构造类型的一种,由基本类型的数据按照顺序组合而成 数组:相同类型的数据的集合,数组中每个数据称为数组的元素。 用来处理类型相同的一批数据
引例: 处理某班10名学生某门课的成绩,求所有学生的平均成绩及最高成绩 main( ) { float sum, ave, a1, a2, a3,a4,a5,a6,a7,a8,a9, a10; scanf(“%f%f%f%f%f”,&a1,&a2,&a3,&a4,&a5); scanf(“%f %f%f%f%f”,&a6,&a7,&a8,&a9,&a10); sum=a1+a2+a3+a4+a5+a6+a7+a8+a9+a10; ave=sum/10; max = a1 ; if (a2> max ) max=a2; if (a3> max ) max=a3; ……….. ‘ 实际程序是不能这样写 } 改程序 特别冗长,假设不是10个数,而是100个呢?按照上面的方法编写程序是不现实的。 该题目的问题在于 一个变量在一个时刻只能存储一个数据。如果要同时存储一个系列的数据,就需要使用一种新的数据类型—数组
引例: 使用数组! 思考: 学生成绩的下标是可以变化的 ai ( i=1,2,…..10 ) 能否使用循环来写程序? 思考: 学生成绩的下标是可以变化的 ai ( i=1,2,…..10 ) 能否使用循环来写程序? C语言中如何表示下标变量呢? 使用数组! 能否有一种数据,使我们像数学中的下标一样使用数据呢?无法表示上标、下标,就用【下标】这样的形式来表示。
5.1 概 述 数组:相同类型的数据的集合称为数组,数组中每个数据称为数组的元素。 按照元素的数据类型,数组可分为: 整形数组、 字符数组、 实型数组、指针数组、结构体数组等各种类别。 数组在内存中占用一片连续的存储单元,最低地址对应于数组的第一个元素,最高地址对应于最后一个元素, 在C语言中, 数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或是构造类型。因此按数组元素的类型不同,
5.2 一维数组 5.2.1 一维数组定义 一维数组:只有一个下标的数组。 一般形式为:类型符 数组名 [常量表达式] ; eg: eg: 5.2 一维数组 5.2.1 一维数组定义 一维数组:只有一个下标的数组。 一般形式为:类型符 数组名 [常量表达式] ; 数组的类型实际上是指数组元素的取值类型。对于同一个数组,其所有元素的数据类型都是相同的。 表示数组元素的个数,可以是整形常量、符号常量、常量表达式,不能为变量 1、数组名不能与同一函数中其它变量名相同 2、数组名代表数组在内存中存放的首地址 eg: #define N 30 int a[N]; #define FD 5 int a[3+2],b[7+FD]; 是合法的。但是下述说明方式是错误的。 int n=5; int a[n]; 什么是下标呢?例如前面的例题5-1中 有10个变量a1、a2、a3.a10,可以构成一个整形数组,这个数组中有十个元素,那么a就可以代表数组的名字,每一个元素可以用a+下标i的方法来表示。 c语言中无法表示下标,所以就用a[i] 来表示。 讲数组名时,要讲到数组在内存中的保存方法。按下标由小到大存放。 eg: int n = 30 ; int a[n]; 例如: int a[10];
我们定义数组 int a[10]; 后,计算机会干什么呢? 内 存 。。。。 首地址a a[0] a[1] 特点: 分配的地址空间连续 内 存 。。。。 我们定义数组 int a[10]; 后,计算机会干什么呢? 首地址a a[0] a[1] 特点: 分配的地址空间连续 按照下标从小到大存放 首地址用数组名表示 a[9]
5.2.3 数组元素的引用 引用一维数组元素的方法为: 数组名[下标] eg:定义数组 int a[10] ; 5.2.3 数组元素的引用 引用一维数组元素的方法为: 数组名[下标] eg:定义数组 int a[10] ; a 数组里面有10个元素,分别为: a[0] a[1] a[2] …. a[8] a[9] 1、C语言中下标从0开始,数组a的最后一个元素是a[9]。 2、C语言中对数组的引用不检验数组边界。 3、下标只能为整型变量、常量或整型表达式。 数组元素是组成数组的基本单元,数组元素也是一种变量。 引用数组元素:例如,a[5],a[i+j],a[i++] 都是合法的数组元素。 2、定义时[ ]中只能是常量,引用时[ ]可以是常量、变量或表达式。 a[1]=3; for(i=0;i<10;i++) s=s + a[i]; a[i-1]=a[i];
× 5.2.3 数组元素的引用 3、在C语言中只能通过下标变量使用单个数组元素,而不能一次引用整个数组。 5.2.3 数组元素的引用 3、在C语言中只能通过下标变量使用单个数组元素,而不能一次引用整个数组。 for(i=0; i<10; i++) printf("%d",a[i]); 下面的写法是错误的: printf("%d",a); × 3、C语言中对数组的引用不检验数组边界 即当引用时下标超界时(下标小于0或大于上界),C系统虽然不出错,但可能使其它变量的数组甚至程序代码被破坏,使得程序运行中断或输出错误的结果。
5.2.2 一维数组的初始化 数组元素赋初值有两种方法: 数组定义时初始化、 赋值语句初始化。 1、数组定义时初始化 5.2.2 一维数组的初始化 数组元素赋初值有两种方法: 数组定义时初始化、 赋值语句初始化。 1、数组定义时初始化 数组初始化的一般形式为: 类型符 数组名[常量表达式]={值,值,……,值}; 例如: int a[10]={ 3,5,2,8,5,6,3,7,8,0 }; 注意: 如果只给部分元素赋初值,剩余元素默认为0; 给全部元素赋初值时,元素个数可以省略。 初值的个数必须小于元素的个数。 如果不赋初值,数组元素是随机数。 相当于a[0]=3; a[1]=5;... a[9]=0;
5.2.2 一维数组的初始化 C语言对数组的初始赋值的几点规定: 5.2.2 一维数组的初始化 C语言对数组的初始赋值的几点规定: 1)可以只给部分元素赋初值。当{ }中值的个数少于元素个数时,只给前面部分元素赋值。 例如: int a[10]={0,1,2,3,4}; 2)只能给元素逐个赋值,不能给数组整体赋值。 例如给十个元素全部赋1值,只能写为: int a[10]={1,1,1,1,1,1,1,1,1,1}; 而不能写为: int a[10]=1;× int a[10]={0,1,2,3,4}; 表示只给a[0]~a[4]5个元素赋值,而后5个元素自动赋0值。
× 5.2.2 一维数组的初始化 (3)如给全部元素赋值,则在数组定义时,可以不给出数组元素的个数。 5.2.2 一维数组的初始化 (3)如给全部元素赋值,则在数组定义时,可以不给出数组元素的个数。 例如: int a[5]={1,2,3,4,5}; 可写为: int a[ ]={1,2,3,4,5}; (4)当{ }中值的个数多于元素个数时, 系统出错。 例如: × int a[5]={1,2,3,4,5,1 };
5.2.2 一维数组的初始化 2、使用赋值语句初始化 用赋值语句初始化是在程序执行过程中实现的。 例如:int a[10]; 5.2.2 一维数组的初始化 2、使用赋值语句初始化 用赋值语句初始化是在程序执行过程中实现的。 例如:int a[10]; a[0]=5;a[1]=8;a[2]=9; eg:给数组a中的10个元素全部赋值为1. eg:接收用户从键盘上输入的10个数据,并赋值给数组a。 int a[10],k; for(k=0;k<10;k++) scanf(“%d”,&a[k]); int k,a[10]; for(k=0;k<10;k++) a[k]=1; 只能对单个元素赋值,不能对数组整体赋值。
5.2.4 一维数组的基本操作 1. 给数组元素输入数据、输出数组元素的值,使用循环 int a[10],i,; 5.2.4 一维数组的基本操作 1. 给数组元素输入数据、输出数组元素的值,使用循环 int a[10],i,; for(i=0;i<10;i++) scanf(“%d”,&a[i]); for(i=0;i<10;i++) printf(“%d”,a[i]);
循环初始化:i=0、max=a[0]、imax=0 5.2.4 一维数组的基本操作 例5-2 输入N个数据存入数组中,输出其中最大元素及其下标 开始 输入N个数组元素 循环初始化:i=0、max=a[0]、imax=0 If (i<N) If (a[i]>max) max=a[i]; imax=i; i++ Y 打印、退出 N
5.2.4 一维数组的基本操作 #define N 10 main( ) { int i, imax, max , a[N]; 5.2.4 一维数组的基本操作 #define N 10 main( ) { int i, imax, max , a[N]; printf("Enter %d Numbers\n",N); /* 提示输入数据 */ for(i=0;i<N;i++) scanf("%d",&a[i]); max = a[0]; / * 假设第0元素就是最大元素 */ imax= 0; for(i=1;i<N;i++) if (a[i] > max) { max = a[i]; imax = i; } printf(" The Max Numbwer a[%d]=%d\n",imax,max); }
5.2.4 一维数组的基本操作 5-3 一维数组的倒置 编程分析:将第0个元素与最后1个元素的交换、第1个元素与倒数第2个元素的交换、……、即第 i 个与第 n-i-1 个元素的交换,直到i<n/2。 A[0]
5.2.4 一维数组的基本操作 一维数组的倒置 #define N 10 main() { int i,t,a[N]; 5.2.4 一维数组的基本操作 一维数组的倒置 #define N 10 main() { int i,t,a[N]; for(i=0;i<N;i++) scanf("%d",&a[i]); for(i=0;i< N/2;i++) /* 将数组倒置 */ { t=a[i]; a[i]=a[N-i-1]; a[N-i-1]=t; } for(i=0;i<N;i++) /* 输出倒置后的数组 */ printf("%d ",a[i]);
例5-4 利用数组求Fibonacci数列的前20个数,并按每行5个数的格式输出,数列的第1、2两个数均为1. 公式:当i>=2时f[i]=f[i-1][i-2]; 打印输出:5个数为一行,所以当i%5==0时要换行。
#include <stdio.h> main( ) { int i; int f[20]={1,1}; for (i=2;i<20;i++) f[i] = f[i-1] + f[i-2] ; for(i=0;i<20;i++) { if (i%5==0) printf("\n"); printf("%12d", f[i]); }
5.2.5 一维数组的应用举例 例5-5 求某班20个学生某门课程考试的平均成绩及高于平均成绩的学生人数。 #define NUM 20 /* 声明代表班上学生人数的符号常量*/ main() { int i,n; /* n存放高于平均成绩的学生人数*/ float a[NUM], sum,aver; sum = 0; /* 给sum赋初值 */ for(i=0;i<NUM;i++) /* 循环输入学生成绩,并求和*/ { scanf("%f", &a[i]); sum =sum + a[i]; /* 求班总成绩 */ } aver = sum / NUM; /* 计算机平均成绩 */ n = 0; for(i=0;i<NUM;i++) /* 统计高于平均成绩的人数*/ if (a[i]>aver) n++; printf("The Class Average Score is:%f\n" ,aver); printf("The Total Number is:%d\n",n);
5.2.5 一维数组的应用举例 例5-5 输入某班20个学生某门课程考试的成绩,统计0~9,10~19,20~29,….80~89,90~99分数段及100分的学生人。 假设使用a[i]存放成绩,(i:0-19)。 用数组b[10]来存各分数段的人数,用b[0]存0~9分的人数,b[1]存10~19分的人数,…b[9]存90~99分的人数,b[10]存100分的人数。 规律:b[k] 的 k 恰好是 分数/10 得到的整数部分。 所以:for (i=0;i<20; i++) { k=a[i] /10; b[k]=b[k]+1; }
5.2.5 一维数组的应用举例 #define NUM 20 /* 声明代表班上学生人数的符号常量*/ main() { int a[NUM], b[11]={0} , i ,k; float sum,aver; for(i=0;i<NUM;i++) /* 循环输入学生成绩 */ scanf("%d", &a[i]); for(i=0 ; i<NUM; i++) /* 统计各分数段的人数 */ { k = a[i] / 10; b[k] = b[k] + 1; } for(i=0;i<10;i++) /* 打印输出各分数段的学生人数 */ printf("%2d --%2d = %d\n", i * 10 , i * 10 + 9 , bn[i]); printf( " 100---= %d " ,b [i]); 程序运行结果如下:
5.3 二维数组与多维数组 二维数组的定义 定义方式: 数据类型 数组名[常量表达式1][常量表达式2]; 例 int a[3][2]; int c[2][3][4] 1 2 3 4 5 6 7 ………... 20 21 22 23 c[0][0][0] c[0][0][1] c[0][0][2] c[0][0][3] c[0][1][0] c[0][1][1] c[0][1][2] c[0][1][3] c[0][2][0] c[0][2][1] c[0][2][2] c[0][2][3] c[1][0][0] c[1][0][1] c[1][0][2] c[1][0][3] c[1][1][0] c[1][1][1] c[1][1][2] c[1][1][3] c[1][2][0] c[1][2][1] c[1][2][2] c[1][2][3] 二维数组的定义 定义方式: 数据类型 数组名[常量表达式1][常量表达式2]; 元素个数=行数*列数 列数 行数 例 int a[3][2]; 数组元素的存放顺序 原因:内存是一维的 二维数组:按行序优先、最右下标变化最快 int a[3][2] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1] 1 4 5 2 3 a[0][0] 二维数组经常用来描述 二维表格、矩阵等。 例如 矩阵 a[0][0] a[0][1] a[1][0] a[1][1] a[2][0] a[2][1]
二维数组理解 a[0] 例 int a[3][4]; a[1] a[2] 注意:a是二维数组的名字,a[0]等也是一维数组的名字 1 4 5 1 4 5 2 3 a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[0][0] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3] a[1][2] 6 7 10 11 8 9 a[0] a[1] a[2] 二维数组a是由3个元素组成 例 int a[3][4]; a[0] a[1] a[2] 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] 行名 数组名字代表数组元素首地址。所以a【0】 是第一行数组的首地址。所以a=a【0】=&a【0】【0】 将每一行看成为一个元素,所以整个数组由三个元素构成a[0]\a[1]\a[2],而每一个元素 又是4个元素的数组。 每个元素a[i]由包含4个元素 的一维数组组成 注意:a是二维数组的名字,a[0]等也是一维数组的名字
5.3.3 二维数组的初始化 二维数组可按行分段赋值,也可按行连续赋值。 1. 按行分段赋值可写为 5.3.3 二维数组的初始化 二维数组可按行分段赋值,也可按行连续赋值。 1. 按行分段赋值可写为 int a[2][3]={ {80 , 75 , 92 },{ 61 , 65 , 71 } } 2. 按行连续赋值可写为(内存存放) int a[2][3]={ 80,75,92,61,65,71 }; 这两种赋初值的结果是完全相同的。 行与行之间用 { }括起来,用,分隔。 每一行的元素之间用,分隔开。 注意:如果某一行的元素全部为0,不可以不写。需要写至少1个 按行分段赋值方法比较直观,也就是 将第一个{}中数值付给第一行,第二个{}中数值付给第2行。。。 第二种方法不直观,数据较多时容易遗漏。 规则:按照二维数组在内存中的存放顺序给元素赋值。 如果初值个数少于元素个数,没有初值对应的元素自动附0. 每个{ }中都不能没有数值。
可以给部分元素赋值,其它元素默认0值。 例 int a[2][3]={{1,2},{4}}; a[0][0] a[0][1] a[0][2] 4 部分初始化 例 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[ ][ 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 第一维长度省略初始化 注意:如果某一行的元素全部为0,不可以不写。需要写至少1个
5.3.2 二维数组元素的引用 形式: 数组名[下标][下标] 下标必须是整数 5.3.2 二维数组元素的引用 下标必须是整数 形式: 数组名[下标][下标] 例如:a[1][2]=10; 是将第一行第二列的元素赋值为10 eg:给二维数组a输入数据 定义数组为int a[N][M],N和M为符号常量,程序段如下: for(i=0;i<N;i++) for(j=0;j<M;j++) scanf(“%d”,&a[i][j]); { } 二维数组的操作一般需要使用二重循环。
例5-7 输出下面二维数组中的最大元素及其下标。 max:最大元素值,row,column:最大值所在行、列号。 main( ) { int i,j,row,colum,max; int a[3][4]={{12,23,3,5},{45,32,56,6},{6,16,34,21}}; max=a[0][0]; row=0;colum=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); }
例5-9 设某一学习小组有5个人,每个人有三门课的成绩。求每人的平均成绩。 学号 Math En C NO0 80 75 92 NO1 61 65 71 NO2 59 63 70 NO3 85 87 90 NO4 76 97 85 编程分析: 使用二维数组a[5][3]存放5个人3门课成绩 一维数组aver[5]存放所求得每个学生的平均成绩 for ( j=0;j<3;j++) sum=sum+a[i][j]; aver[i]=sum/3;
思考与讨论: 例4-5 一个学习小组有5个人,每个人有三门课的考试成绩。求每人的平均成绩。 void main() 例4-5 一个学习小组有5个人,每个人有三门课的考试成绩。求每人的平均成绩。 void main() { int i , j, sum, a[5][3], aver[5]; printf("input score\n"); for(i=0;i<5;i++) { sum=0; for(j=0; j<3; j++) { scanf("%d",&a[i][j]); sum=sum+a[i][j]; } aver[i]=sum/3; printf("NO%d aver=%5d\n",i, aver[i]); 思考与讨论: 如果数组a定义为“int a[5][4];”,将每位学生的平均成绩存放在二维数组的第4列,上面的程序将如何修改? aver[i]=sum/3 换成 a[i][3]=sum/3;
5.3.4 二维数组的基本操作 计算两矩阵相乘 设有矩阵A有M*L个元素,矩阵B有L*N个元素,则矩阵C=A*B有M*N个元素。矩阵C中任一元素: (i=1,2,…,m; j=1,2,…,n) 例5-7 求下面两矩阵的乘积矩阵
#define M 2 #define N 3 #define P 3 void main() { int i,j,k,c[M][N]; int a[M][P]={{1,5,6},{3,2,8}}; int b[P][N]={{1,2,3},{4,5,6},{7,8,9}}; for(i=0;i<M;i++) for(j=0;j<N;j++) { c[i][j] = 0; for(k=0;k<P;k++) c[i][j]= c[i][j] + a[i][k] * b[k][j];} for(i=0;i<M;i++) { for(j=0;j<N;j++) printf("%d ",c[i][j]); printf("\n"); }
5.3.4 二维数组的基本操作 例5.8 矩阵的转置 方阵:以对角线为基准,对应元素交换。 a[i][i]不变 例5.8 矩阵的转置 方阵:以对角线为基准,对应元素交换。 a[i][i]不变 4:由a[0][1] <->a[1][0] 8:由a[1][2] <->a[2][1] 所以只需a[i][j] <->a[j][i] for(i=1; i<N; i++) for(j= 0; j<i; j++) { t=a[ i ][ j ]; a[ i ][ j ]=a[ j ][ i ]; a[ j ][ i ]=t; } for( i=0; i<N; i++) for(j=i+1 ; j<N; j++) { t= a[i][j];
例5-9 将下面矩阵的转置存放,并打印输出 #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<N; i++) for(j=i+1; j<N; j++) {t = a[ i ][ j ]; a[ i ][ j ] =a[ j ][ i ]; a[ j ][ i ]= t; } for(i=0; i<N; i++) /* 打印输出转置后的矩阵 */ { for(j=0; j<N; j++) printf("%d ",a[i][j]); printf("\n"); /* 换行 */
5.3.4 二维数组的基本操作 如果不是方阵,则要定义另一个数组。设a是M*N的矩阵,要重新定义一个N*M的二级数组b 算法: b[j][i] = a[i][j] for(i=0; i<M; i++) for(j= 0; j<N; j++) b[j][i]=a[i][j];
5.4 数组与指针 5.4.1 一维数组与指针 数组在内存单元中占用一片连续的存储空间,第一个元素的存储地址即为整个数组的地址,也就是数组的指针。 数组里每一个元素的地址就是元素的指针。 a [0] a [1] a [2] a [3] a [9] ... 整型指针p &a [0] p 定义一个一维数组 int a[10]; 则:C语言规定其数组名就是数组在内存中的首地址。 P可以指向数组中的某一个元素。例如p=&array【3】。 定义指针变量 int *p; 令 p=&a [0]; 或者 p=a; 或 int *p=&a[0]; 或 int *p=a;
5.4.1 一维数组与指针 如果定义 int a[10], *p; p=a; 那么 p+i 指向哪里? C规定:如果指针变量 p 已指向数组中的一个元素,则 p+1 指向同一数组中的下一个元素,而不是将p的值或者地址简单加1 注意:不是将p值简单加1。如果数组元素是整型,p+1表示p的地址加2;如果数组元素是实型,p+1表示p的地址加4;如果数组元素是字符型,p+1表示p的地址加1。 例:如果数组元素是float型的,每个数组元素占4个字节,则p+1 是使p的值(地址) 加4个字节,指向下一个元素
a[i] p[i] *(p+i) *(a+i) 数组元素的引用方法:4种 [] 变址运算符 a[i] *(a+i) a[0] a[1] a[2] a[3] a[9] ... a a+9 a+1 a+2 地址 元素 下标法 a[0] a[1] a[2] a[3] a[9] ... p p+9 p+1 p+2 地址 元素 指针法 *p *(p+1) *(p+2) *(p+9) *a *(a+1) *(a+2) *(a+9) p[0] p[1] p[2] p[9] a[i] p[i] *(p+i) *(a+i)
例 int a[ ]={1,2,3,4,5,6,7,8,9,10},*p=a,i; 数组元素地址的正确表示: (A)&(a+1) (B)a++ (C)&p (D)&p[i] 数组名是地址常量 p++,p-- () a++,a-- () a+1, *(a+2) () 数组元素的正确表示: (A)a[i] (B)p[i] (C)*(p+i) (D)*(a+i)
例5-10 输入N个数据存入数组中,输出其中的最大元素及下标。 #define N 10 main( ) { int i, imax, max, a[N], *p; /*imax代表最大值无素所在的位置*/ for(p=a;p<a+N;p++) scanf("%d", p ); max = a[0]; imax= 0; /*假设第0元素就是最大元素*/ p=a; /*注意p的当前值*/ for(i=0;i<N;i++,p++) /*此语句可以写for(i=0;p<a+N;i++,p++)*/ if (*p > max) { max = *p); imax = i; } printf(" The Max Numbwer a[%d]=%d\n",imax,max); } for (i=0;i<N;i++) scanf(“%d”,&a[i]); for (i=0;i<N;i++) scanf(“%d”, a+i); if ( a[i]>max ) {max=a[i]; imax=i;} if ( *(a+i)>max ) {max=*(a+i); imax=i;}
注意指针变量的当前值 p=a; //指针变量赋值 #include <stdio.h> void main() { 例5-11,输入十个数据存入数组a中,然后打印输出数组a中的元素。请指出下列程序的错误,并加以改正。 #include <stdio.h> void main() { int a[10], i, *p; //指针变量的定义 p=a; //指针变量赋值 for(i=0;i<10;i++) scanf(“%d”,p++ ); for(i=0;i<10;i++, p++) printf("%d",*p ); //指针变量使用 } 指针变量可以指到数组后的内存单元
5.4.1 一维数组与指针 指向数组元素的指针的一些运算 设有定义“int a[10],*p=a;”,则对指向数组的指针变量的一些操作运算: 5.4.1 一维数组与指针 指向数组元素的指针的一些运算 设有定义“int a[10],*p=a;”,则对指向数组的指针变量的一些操作运算: 若p1与p2指向同一数组,p1-p2=两指针间元素个数 p1+p2 无意义
例:5-12 指向数组的指针变量的运算 #include <stdio.h> void main( ) { int a[6]={2,4,6,8,10,12},*p; p=a+2; printf("%d\n",*p++); printf("%d\n",*(p++)); printf("%d\n",*++p); printf("%d\n",(*p)++); printf("%d\n",*p); }
例 输出数组中的所有元素 (使用4种数组元素的引用方法) (1)通过数组下标引用数组元素 main() { int a[5], i; for(i=0;i<5;i++) scanf(“%d”,&a[i] ); printf("%d", a[i] ); } 如果将最后2语句换成 for(p=a,i=0;i<5;i++) printf("%d", p[i] );
例 2)通过数组名计算数组元素地址,找出元素的值 例 2)通过数组名计算数组元素地址,找出元素的值 main() { int a[5], i; for(i=0;i<5;i++) scanf(“%d”,&a[i] ); printf("%d", *(a+i) ); } a[0] a[1] a[2] a[3] a[4] 1 2 3 4 5 p 例 3)通过指针变量引用数组元素 main() { int a[5], i; int *p; //指针变量的定义 for(i=0;i<5;i++) scanf(“%d”,&a[i] ); for(p=a; p<(a+5); p++) //指针变量赋值 printf("%d\n",*p ); } //指针变量使用 如果将最后2语句换成 for(p=a; a<(p+5); a++) printf("%d\n", *a ); 上述替换是不对的:a代表数组的首地址,是不变的;p是一个指针变量,可以指向数组中的任何元素
5.4.2 二维数组与指针 1.二维数组的地址 二维数组 :int a[3][4]; 即: 5.4.2 二维数组与指针 1.二维数组的地址 二维数组 :int 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] a[0] a[1] a[2] a 二维数组a可理解为有三个元素(a[0]、a[1]、a[2])的一维数组,每一个元素又是一个有4个元素的一维数组。 例如:一维数组a[0]的元素为 a[0][0],a[0][1],a[0][2],a[0][3]。 a+0 中的+ 表示 指向下一个元素的地址。 a+0、a+1、a+2 分别表示a[0] \a[1]\ a[2]行的首地址 a[0]+0、 a[0]+1、 a[0]+2…分别表示a[0]行每一个元素的地址
二维数组a的指针。假设第一个元素的地址是0x2000 5.4.2 二维数组与指针 2 3 1、a 与 a[0] 与&a[0][0] 数值上相同,但是增量不同。 2、引用元素可以有如下方法: 例如引用a[1][2]元素,方法: a[1][2] 或者 *(a[1]+2) 或者 *(*(a+1)+2) 从右边往左边讲 首先:元素按行序 连续存放。 因为元素是int类型,所以每一个元素的地址差为2. 其次:能否用数组名表示地址,即a【0】是数组名 表示首元素地址, a【0】+1就是该行数组中第1个元素的地址。&a[0][1]. 第三: a是三个元素数组的名字,将三行各位一个整体,所以a中有3个元素a【0】 a【1】 a【2】。那么a即a【0】行的地址, a+1即a【1】行的地址。 二维数组a的指针。假设第一个元素的地址是0x2000
5.4.2 二维数组与指针 说明: a+0表示第0行的首地址,与a[0]、或&a[0][0]的值相同,但意义及参与的运算不同。 5.4.2 二维数组与指针 说明: a+0表示第0行的首地址,与a[0]、或&a[0][0]的值相同,但意义及参与的运算不同。 a+1表示第1行元素的首地址,与a[1]、或&a[1][0]的值相同。 a+2表示第2行元素的首地址,与a[2]、或&a[2][0]的值相同。 a[0]相当于&a[0][0], a[1]相当于&a[1][0],a[2]相当于&a[2][0] 根据一维数组的表示方法,则有: a[0]+1:表示一维数组中第二个元素的地址,相当于&a[0][1]。 a[0]+2:表示一维数组中第三个元素的地址,相当于&a[0][2]。 a[1]+1:表示二维数组中第二个元素的地址,相当于&a[1][1]。 访问二维数组元素可以使用下面的方法: a[1][2] *(a[1]+2) *(*(a+1)+2)
行指针变量:指向二维数组中的一行的指针变量 定义: 类型标识符 (*指针变量名)[元素个数] int (*p)[4]; 定义指针变量p指向了 有4个整形元素的一行。
5.5 字符数组与字符串 5.5.1 字符数组与初值化 一、字符数组的定义 二、字符数组的初始化 :定义时初始化或赋值语句初始化 例如: char c[10]; 定义有10个元素的数组 二、字符数组的初始化 :定义时初始化或赋值语句初始化 (1)定义时赋初值。部分赋值时,多余元素为“空”(‘\0’), 当初始化数据多于元素个数时,将出错。 完全赋值时,元素个数可以省略。 例如: char c[10]={'c', ' ' , 'p' , 'r' , 'o' , 'g' , 'r' , 'a' , 'm'}; 用来存放字符数据的数组叫字符数组。字符数组每一个元素存放一个字符。 (2)使用赋值语句逐个元素赋值,例如: char c[10]; c[0]='I'; c[1]=' '; c[2]='a'; c[3]='m'; c[4]=' ';
5.5.2 字符数组的引用 char c[10]; 引用:c[i] 改成下列程序行不行? while ( a[i] != ‘\n’ ) { 5.5.2 字符数组的引用 char c[10]; 引用:c[i] 改成下列程序行不行? while ( a[i] != ‘\n’ ) { scanf( “%c”, &a[i] ); if ((a[i]>='A')&&(a[i]<='Z')) a[i]=a[i]+32; i++; } void main( ) { char a[40]; int i=0, j; do {scanf( “%c”, &a[i] ); if ((a[i]>='A')&&(a[i]<='Z')) a[i]=a[i]+32; i++; } while ( a[i-1] != ‘\n’ ) ; for (j=i-2;j>=0;j--) printf("%c",a[j]); /*每次下标减1,保证逆向输出*/ } 例5-17 从键盘上输入一行字符(不多于40个,以回车换行符作为输入结束标记),将其中的大写字母变为小写字母,其他字符不变,然后逆向输出。 改成while循环不可以。因为while后条件判断的时候,a[i]里实际上还没有赋初值呢。 a[i]=((a[i]>='A')&&(a[i]<='Z'))? a[i]+32:a[i]
5.5.3 字符串与字符数组 字符串是一种以“\0”结尾的字符数组。 在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。 字符串总是以‘\0’作为串的结束符。因此当把一个字符串存入一个数组时, 也把结束符‘\0’存入数组,并以此作为该字符串是否结束的标志。 字符串是一种以“\0”结尾的字符数组。
c[ ]=“welco” c=“welco” 5.5.3 字符串与字符数组 只有在初始化的时候可以用字符串给字符数组赋值。 在语句里不能这么写: c[ ]=“welco” c=“welco” 3. 对字符数组只能按字符逐一赋值。 4. C是个常量,不能赋值 1、字符数组可以用字符串来初始化 例如: 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 对字符数组初始化 可以使用单个字符赋值 或者 用字符串赋值的方法。 注意:下面的定义是不同的。 char c1[5]={'G','o','o','d','!'}; char c2[ ]={"Good!"};
5.5.4 字符数组的输入输出 字符数组的输入输出一般采用下面两种方法: 1、用“%c”格式符逐个字符输入输出。 2、用“%s”格式符按字符串输入输出。 用字符数组名,不要加& 输入串长度<数组维数 遇空格或回车结束 自动加‘\0’ 例 用%c main() { char str[5]; int i; for(i=0;i<5;i++) scanf(“%c”, &str[i]); printf(“%c”, str[i]); } 例 用%s main() { char str[6]; scanf(“%s”, str); printf(“%s”, str); } (5)输入时,遇回车、空格结束,但获得的字符中不包含回车、字符本身(0x0D,0x0A),而是在字符串末尾添'\0'。因此,定义的字符数组必须有足够的长度,以容纳所输入的字符。(如,输入5个字符,定义的字符数组至少应有6个元素)。 用字符数组名, 遇‘\0’结束, 不包含'\0'。
例子 main() { int i; char a[5]; scanf("%s",a); for(i=0;i<5;i++) printf("%c",a[i]); } 输入字符串长度<数组维数 h e l \0 h e l l \0 h e l l o 运行情况: (1)若输入 hel , 正常 (2)若输入 hell , 正常 (3)若输入 hello , 用%s 输出时,会出现问题 %s格式输入的时候,只以空格或者回车作为唯一结束标志。但是我们需要预留足够的存储空间来存放输入的串,否则会受到其他数据的干扰。 用%c打印 用%s打印 注意:当输入长度>数组长度时,结果会不确定
当输入长度>定义数组长度时的错误情况 main() { int i; char a[5],b; scanf("%s",a); b='A'; printf("%s",a); } a h e l o b i j \0 A 不同系统下得到的结果是不一样的,得到helloij的是VC中运行的。改变的是Wintc中的结果。
例 字符串输入举例 #include <stdio.h> main() { char a[15],b[5],c[5]; 例 字符串输入举例 #include <stdio.h> main() { char a[15],b[5],c[5]; scanf("%s%s%s",a,b,c); printf("a=%s\nb=%s\nc=%s\n",a,b,c); } scanf中%s输入时,遇空格或回车结束 运行情况: 输入:How are you? H o w \0 a r e \0 y o u ? \0
5.5.4 字符数组的输入输出 说明: (1)“%s”格式输出字符串时,printf()函数的输出项是字符数组名,而不是元素名。 char c[ ] = "Good!"; printf("%s",c); printf("%c",c[0]); printf("%s",c[0]); (2)“%s”格式输出时,即使数组长度大于字符串长度,遇'\0'也结束。 例如:char c[10] = {"Good!"}; printf("%s",c); /*只输出5个字符 */ (3)“%s”格式输出时,遇第一个'\0'时结束。 例如:char c[ ] = {"Good!\0boy"}; printf("%s",c); 只输出结果是:Good! printf(“%s”,c); 对 printf(“%c”,c[0]); 对 printf(“%s”,c[0]);错 printf(“%s”,&c[0]); 对 printf(“%s”,c+3); 对
5.6 字符串与字符指针 使用字符数组处理字符串: main( ) { char string[ 30]; 5.6 字符串与字符指针 使用字符数组处理字符串: main( ) { char string[ 30]; scanf(“%s”,string); printf(“%s\n”,string); } 1、如果输入的字符串只有2个字符 2、给字符数组重新赋值方便吗? 字符数组在使用的过程中 使用字符串重新赋值不方便,例 a【10】=“china” 这是不允许的。所以,一个数组只能被一个字符串使用,太浪费。 另外字符数组一直占用存储空间,如果字符串中元素个数不确定的话,会造成空间不足或者浪费。所以对字符串的处理,我们更多的使用字符指针。 有什么不足? C语言中还可以使用字符指针来处理字符串。
5.6.1 指向字符串的指针 字符指针初始化:把字符串首地址赋给string 注意 :可以如此赋初值 5.6.1 指向字符串的指针 I l o v e C h i string n ! a \0 字符指针初始化:把字符串首地址赋给string char *string; //字符指针变量定义 string=“I love China!”; 注意 :可以如此赋初值 char *str=“chain”; char *str={“chain”}; char *str; str=“chain”;不能加{ }了 例 main( ) { char *string=“I love China!”; printf(“%s\n”,string); string+=7; while(*string) { putchar(string[0]); string++; } string *string!=0 注意:这里给字符指针赋值,实际上是把字符串的首地址付给了指针变量,并么有把字符串的元素赋值。 引用字符串中的字符:可以用下标方法:string[0] 或者指针方法*string 字符指针变量:存放的仍然是某一个字符元素的地址
指针法引用字符串元素 %s格式打印整个字符串 下标法引用字符串元素 %c格式打印字符元素 例5-18 将字符串a复制到字符串b main() { char a[ ]="i am a boy.",b[20]; int i; for(i=0; *(a+i) != ′\0′; i++) *(b+i)=*(a+i); *(b+i)=′\0′; printf("string a is:%s\n",a); printf("string b is:"); for(i=0; b[i]!=′\0′; i++) printf("%c",b[i]); } 指针法引用字符串元素 %s格式打印整个字符串 下标法引用字符串元素 赋值:可以选用的方法: for(i=0;a[i]!='\0',i++) b[i]=a[i]; while (a[i]!='\0') {b[i]=a[i]; i++; } %c格式打印字符元素
改进: 使用指针变量法:将字符串a复制为字符串b(2) #include "stdio.h" void main() {char a[]="I am a boy",b[20],*p1,*p2; int i; p1=a; p2=b; for( i=0;*p1!='\0';p1++,p2++ ) *p2=*p1; *p2='\0'; printf("string a is:%s\n",a); printf("string b is:%s\n",b); } 指针法引用字符串元素 %s格式打印整个字符串
5.6.2 使用字符串指针变量与字符数组的区别 判断正误 5.6.2 使用字符串指针变量与字符数组的区别 1)字符串指针变量本身是一个变量,用于存放字符串的首地址。字符数组是由若干个数组元素组成的,它可用来存放整个字符串。 2)对字符串指针方式“char *ps=”C Language“;” 可以写为“char *ps;ps=”C Language“;”, 而对数组方式“char st[ ]={"C Language"};” 不能写为“char st[20];st={"C Language"}”。 char str[]={“Hello!”}; () char str[]=“Hello!”; () char str[]={‘H’,‘e’,‘l’,‘l’,‘o’,‘!’}; () char *cp=“Hello”; () int a[]={1,2,3,4,5}; () int *p={1,2,3,4,5}; () char str[10],*cp; int a[10],*p; str=“Hello”; () cp=“Hello!”; () a={1,2,3,4,5}; () p={1,2,3,4,5}; () 判断正误 而字符串本身是存放在以该首地址为首的一块连续的内存空间中并以'\0'作为字符串的结束。
5.6.3常用的字符串处理函数 字符串输出函数puts 格式:puts(字符数组) 功能:向显示器输出字符串(输出完,换行) 说明:字符数组必须以‘\0’结束 参数为:字符数组名字 或者 字符指针变量 char *s=“chi\0na”; char ch[ ]={“china”}; puts(s); puts(ch); 等价于 printf(“%s\n”,ch); chi china 字符串输入函数gets 格式:gets(字符数组) 功能:从键盘输入一以回车结束的字符串放入字符数组中, 并自动加‘\0’ 说明:输入串长度应小于字符数组维数
例 #include <stdio.h> main( ) { char string[80]; gets(string); puts(string); } 输入: How are you? 输出: How are you ? 输入改成为 scanf(“%s”,string); 注意:用scanf和gets 输入字符串时, 结束字符不相同
例 char str1[20] ={“Hello!”}; 常用的字符串处理函数(1) 字符串连接函数strcat 格式:strcat(字符数组1,字符数组2) 功能:把字符数组2连到字符数组1后面 返值:返回字符数组1的首地址 说明:字符数组1必须足够大 连接前,两串均以‘\0’结束;连接后,串1的‘\0’取消, 新串最后加‘\0’ 例 char str1[20] ={“Hello!”}; char str2[ ]=“china”; printf(”%s”,strcat(str1,str2)); 字符串拷贝函数strcpy 格式:strcpy(字符数组1,字符串2) 功能:将字符串2,拷贝到字符数组1中去 返值:返回字符数组1的首地址 说明:字符数组1必须足够大 拷贝时‘\0’一同拷贝 不能使用赋值语句为一个字符数组赋值 例 判断以下语句是否正确: char str1[20],str2[20]; str1={“Hello!”}; str2=str1; strcpy(str1,”Hello!”); strcpy(str2,str1); str1={“Hello!”}; () str2=str1; () strcpy(str1,”Hello!”); (√) strcpy(str2,str1); (√)
if (strcmp(str1,str2)>0 ) printf(“yes”); (√) 常用的字符串处理函数(2) 字符串比较函数strcmp 格式:strcmp(字符串1,字符串2) 功能:比较两个字符串 比较规则:对两串从左向右逐个字符比较(ASCII码), 直到遇到不同字符或‘\0’为止 返值:返回int型整数,a. 若字符串1< 字符串2, 返回负整数 b. 若字符串1> 字符串2, 返回正整数 c. 若字符串1== 字符串2, 返回零 说明:字符串比较不能用“==”,必须用strcmp 例 char str1[20],str2[20]; if (str1>str2) printf(“yes”); () if (strcmp(str1,str2)>0 ) printf(“yes”); (√) 字符串长度函数strlen 格式:strlen(字符数组) 功能:计算字符串长度 返值:返回字符串实际长度,不包括‘\0’在内 strcmp 实际上返回的是 两个字符串中第一个不相同元素的ascii码之差 例2 长度 答案 1、2、1 遇到\0认为字符串结束 ,后面的就不算在字符串长度内。转义字符也算一个长度。 例 对于以下字符串,strlen(s)的值为: (1)char s[10]={‘A’,‘\0’,‘B’,‘C’,‘\0’,‘D’}; (2)char s[ ]=“\t\\\0will\n”; (3)char s[ ]=“\x69\082\n”; 答案:1 2 1
How are you?Hello! Len1=6,Len2=12,Len3=18 #include <string.h> #include <stdio.h> main() { char str1[] = ”Hello!", str2[] = ”How are you?”,str[20]; int len1,len2,len3; len1=strlen(str1); len2=strlen(str2); if(strcmp(str1, str2)>0) { strcpy(str,str1); strcat(str,str2); } else if (strcmp(str1, str2)<0) { strcpy(str,str2); strcat(str,str1); } else strcpy(str,str1); len3=strlen(str); puts(str); printf(”Len1=%d,Len2=%d,Len3=%d\n”,len1,len2,len3); } 例 strcmp与strlen举例 How are you?Hello! Len1=6,Len2=12,Len3=18
总结:三种对字符串输入输出的方法 假如输入 abcde回车 则输出a b c d e 假如输入a回车b回车c回车后,自动打印 a b c 即输入的个数满足需求后,遇到回车认为结束。否则回车符也作为输入的字符存在。 输入的长度可以>=定义长度,根据定义长度截取。 方法1: #include <stdio.h> main() { int i; char a[5]; for(i=0;i<5;i++) scanf("%c",&a[i]); printf("%c ",a[i]); }
方法2:%s main() { char str[5]; scanf(“%s”, str); //输入以回车、空格结束 printf(“%s”, str); //输出以\0结束 } 方法3:puts、gets <string.h> puts(str); gets(str); 方法2、3输入的长度必须<定义的字符数组的长度。
5.7 指针数组与多级指针变量 5.7.1 指针数组 概念:由指针变量构成的数组 就是指针数组。 定义形式: 5.7.1 指针数组 概念:由指针变量构成的数组 就是指针数组。 定义形式: 类型标识符 *数组名[数组元素个数]; 例如: int *p[4]; 解释:定义一个指针数组p中,有4个指针变量p[0]、 p[1] 、 p[2]、 p[3] ,每一个指针变量都指向整形元素。 指针数组的用途:处理多个字符串。
5.7.1 指针数组 使用二维数组存放多个字符串,浪费较多内存! 例如:char ch[][16]={"Follow me", 5.7.1 指针数组 使用二维数组存放多个字符串,浪费较多内存! 例如:char ch[][16]={"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer Design"} 列宽必须是最长的字符串的长度
5.7.1 指针数组 例5-20 将若干字符串按字母顺序(由小到大)输出。 #include "stdio.h" 5.7.1 指针数组 例5-20 将若干字符串按字母顺序(由小到大)输出。 #include "stdio.h" #include "string.h" void main() {char *temp; int i, j, k, n=5; char *pc[5] ={"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer Design"}; for(i=0; i<n-1; i++) /*n个字符串,外循环n-1次*/ {k = i; for(j=i+1; j<n; j++) /*内循环*/ if (strcmp(pc[k], pc[j]) > 0 ) k = j; if (k!=i) { /*交换pc[i]与pc[k]的指向 */ temp = pc[i]; pc[i] = pc[k]; pc[k] = temp; } } for (i=0; i<n; i++) printf("%s\n", pc[i]);
5.7.1 指针数组 char *pc[5]={… }; 图5-15 指向指针的指针变量与指针数组 图5-16 排好序后指针数组指向的情况
5.7.2 指向指针的指针 x p1 &x 3 例如: int x=3; int *p1=&x ;p1指向x int **p2=&p1; 5.7.2 指向指针的指针 x p1 &x 3 例如: int x=3; int *p1=&x ;p1指向x int **p2=&p1; &p1 p2 存放某个指针变量地址的指针变量被称为指向指针的指针。 指向指针的指针变量的定义形式如下: 类型名 **变量名; 指针变量的值是一个地址,指针变量本身也占据一定的存储空间,它也有自己的地址,也可以存放在另一个指针变量中。
5.7.2 指向指针的指针 例5-21 分析下面例题的输出结果。 #include <stdio.h> void main() 5.7.2 指向指针的指针 例5-21 分析下面例题的输出结果。 #include <stdio.h> void main() {int a=10,*p1=&a,**p2=&p1; printf("p1=%u,&a=%u\n", p1 , &a); printf("p2=%u,&p1=%u\n", p2 , &p1); printf("*p1=%d\n", *p1); printf("**p2=%d\n", **p2); } 指向指针的指针变量。
5.7.2 指向指针的指针 例5-27 用指向指针的指针变量实现例5-25中的字符串排序程序。 程序代码如下: 5.7.2 指向指针的指针 例5-27 用指向指针的指针变量实现例5-25中的字符串排序程序。 程序代码如下: #include "stdio.h" #include "string.h" void main() { char *pc[] ={"Follow me", "BASIC", "Great Wall", "FORTRAN", "Computer Design"}; int i, j, k, n=5; char *temp, **p=pc; for(i=0; i<n-1; i++) /*n个字符串,外循环n-1次*/ { k = i; for(j=i+1; j<n; j++) /*内循环*/ if (strcmp(*(p+k), *(p+j )) > 0 ) k = j; if (k != i) { /*交换pc[i]与pc[k]的指向 */ temp =*(p+i ); *(p+i ) = *(p+k ); *(p+k )= temp; } for (i=0; i<n; i++) printf("%s\n", *(p+i ));
5.7.2 指向指针的指针 图5-15 指向指针的指针变量与指针数组 图5-16 排好序后指针数组指向的情况
5.8 应用程序举例 5.8.1 排序问题:选择排序、冒泡排序 选择法排序(设升序) :每一轮找出最小的数值换到数组最前面。即第一轮找到最小数和a[0]交换位置,第二轮最小数和a[1]交换 a[0] a[1] a[2] a[3] a[4] 75 40 57 38 65 第1次排序 第2次排序 第3次排序 第4次排序 处理成批的数据,要使用数组。 所以:5个元素排序,需要比较4次。假设i表示次数,每一次求出最小值,和a[i]交换
if(a[p]>a[j]) p=j; if( i !=p ) { s=a[i]; a[i]=a[p]; a[p]=s; } for(i=0;i<n-1;i++) { p=i; for(j=i+1;j<10;j++) if(a[p]>a[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]); 选择法排序算法的流程图: P=i,即先假设a[i]就是最小元素。最小元素用a【p】表示 思考:如果按降序排,程序如何修改?
例 5-23 输入20个整数,用选择法由小到大排序,将其以每行10个数据打印输出。 #include <stdio.h> #define N 20 /*声明代表数据个数的符号常量*/ void main() { int i,j,p,t,a[N]; for(i=0;i<N;i++) scanf("%d",&a[i]); for(i=0;i<N-1;i++) /*第i遍*/ { p=i; for(j=i+1;j<N;j++) /*查找最小数的标*/ if( a[p]>a[j] ) p=j; if(p!=i) { t=a[i]; a[i]=a[p]; a[p]=t;} /*交换a[i]和a[p]*/ } for(i=0;i<N;i++) /*输出排序后的数据*/ { printf("%d ",a[i]); if ((i+1)%10==0) printf("\n"); /*输出10数据后换行*/
2.冒泡法排序(升序) 算法 :(将相邻两个数比较,大数交换到后面) 每一轮将相邻两个数比较,小的换到前面,该轮比较结束后,最大的数在最高位置。 n个数共进行n-1趟比较, 在第i趟中要进行n-i次两两比较。
冒泡法从小到大排序20个数值 #include <stdio.h> #define N 20 /*声明代表数据个数的符号常量*/ void main() { int i,j,p,t,a[N]; for(i=0;i<N;i++) scanf("%d",&a[i]); for(i=0;i<N-1;i++) /*第i遍*/ for(j=0;j<N-i-1;j++) if(a[j]>a[j+1]) { t=a[j]; a[j]=a[j+1]; a[j+1]=t; } for(i=0;i<N;i++) /*输出*/ { printf("%d ",a[i]); if ((i+1)%10==0) printf(“\n”); /*10个一行输出*/ }
5.8.2 数据查找 1.顺序查找法(在一列数中查找某数x) 例5-24在n个数据中,查找x,如果有则返回该数据位置。 编程分析:设有n个数据放在a[0]---a[n-1]中,把x与a[i]比较,若相同,查找成功,返回该数的位置;若找不到,则查找失败。 for(i=0;i<N;i++) if (x == a[i]) { index=i; break; }
5.8.2 数据查找 5.24:10个数中查找是否有数据x,如果有,则返回其下标 #define N 10 /* N代表数据的个数 */ void main() { int a[N] = {12,34,1,3,67,89,28,61,9,87}; int index, x, i; scanf("%d",&x); for(i=0;i<N;i++) /* 逐个比较进行查找 */ if (x == a[i]) { index=i; break;} if(i>=N) printf("the number is not found!\n"); else printf("the number is found the no%d!\n",index); }
5.8.2 数据查找 例5-25 使用折半查找法,在一批有序数据数列中查找给定的数x. 2.折半查找法(又叫二分法,只能对有序数列进行查找) 编程分析:设n个有序数(从小到大)存放在数组a[0]----a[n-1]中,要查找的数为x。用变量bot、top、mid 分别表示查找数据范围的底部(数组下界)、顶部(数组的上界)和中间,mid=(top+bot)/2,折半查找的算法如下: mid top bot
void main() { int a[10] = { 1,4,7,13,16,19,28,36,49,60 }; int mid , bot , top , x , i , find; scanf("%d",&x); bot=0; top=9; find=0; while(bot<=top && find==0) { mid=(top+bot)/2; /* 计算中间要比较的元素下标 */ if(x==a[mid]) { find=1; break; } /* 查找成功 */ else if(x<a[mid]) top=mid-1; /* 数据x在下半部分 */ else bot=mid+1; /* 数据x在上半部分 */ } if (find==1) printf("the number is found the no.%d!\n",mid); printf("the number is not found!\n");
5.8.3 插入法 例5-31 把一个给定的数据x按大小顺序插入已排好序的数组中,插入后数组元素仍然有序。 编程分析:首先确定 x 插在数组中的位置 p。 (1)首先确定x插在数组中的位置p,实现的语句如下。 for(p=0; p<n ; p++) if(x<a[p])break; (2)a[p]—a[n]元素向后顺移一个位置以空出a[p]元素放入x, for (i=n; i>= p; i--) a[i]=a[i-1], a[p]=x;
#define N 10 void main() { int a[N+1] = { 1,4,7,13,16,19,28,36,49,60 }; int x , p, i; for (i=0; i<N; i++) /* 输出插入前的数据序列 */ printf(" %d ",a[i]); scanf("%d",&x); for (p=0 ; p<N ; p++) if ( x < a[p] ) break; for(i=N; i>p; i--) /* 将元素往后移,空出x所在的位置 */ a[i]=a[i-1]; a[p]=x; /* 将x插入到数组中 */ for(i=0;i<=N;i++) /* 输出插入后的数据序列 */ }
例5-27 输出一串字符,将其中的英文字母加密,非英文字母不变。 ABCDEFGHIJKLMNOPQRSTUVWXYZ 算法:将每个字母c加3,即用它后面的第3个字母代替c=c+3,当加3之后的数值超过z或者Z,就对c=c+3-26;
#include <stdio.h> main( ) { char st[80]; int i=0; printf("Input a string:"); gets(st); while ( st[i]!='\0‘ ) { if ((st[i]>='A' && st[i]<='Z') || (st[i]>='a' && st[i]<='z')) { st[i]=st[i]+3; if( st[i]>'z' || (st[i]>'Z'&& st[i]<'Z'+3) ) st[i]=st[i]-26; } i++; puts(st);
本章小结 本章介绍了一维、二维数组的定义及应用、数组与指针、字符串与指针等内容。 数组的声明由类型符、数组名、数组长度(数组元素个数)三部分组成。数组元素又称为下标变量。数组的类型是指下标变量取值的类型。 对数组的赋值可以用数组初始化赋值,输入函数动态赋值和赋值语句赋值三种方法实现。对数值数组不能用赋值语句整体赋值、输入或输出,而必须用循环语句逐个对数组元素进行操作。 C语言中,指针是非常重要的内容,它是C语言的特色所在。使用指针可以方便、快捷地访问数组;字符串可以使用字符数组和字符指针来处理,使用指针处理字符串比较使用字符数组处理字符串更加方便。
矩阵补充 M*L矩阵 必须和L*N矩阵相乘,结果为M*N矩阵。 C=a*b= 求Cij: for(k=0;k<L;k++) C[i][j]=C[i][j]+a[i][k]*b[k][j]