? 4.1 一维数组的引出与使用
4.2 二维数组的引出与使用
4.3 字符数组
4.4 数组与函数
4.5 数组与指针
4.6 字符串与指针
4.7 典型例题第四章 简单构造数据类型
4.1 一维数组的引出及使用
4.1.1 一维数组的引出一、引出例,某班有 40名学生,求该班成绩的平均分
#include <stdio.h>
void main( )
{ int j,s,sum=0 ;
float ave ;
for(j=1; j<=40 ; j++)
{ scanf(“%d”,&s);
sum=sum+s;
}
ave=sum/40;
printf(“ave=%f”,ave);
}
这里只使用了一个变量 s,虽然通过循环我们输入了 40个学生的成绩,但循环结束后 s中只是第 40个学生的成绩,前面 39个学生的成绩都没有保存下来如果要求保存这 40名学生的成绩,最后再输出,应该怎么办?
因为现在要保存每个学生的成绩,那就不能只使用一个变量 s了,
而需要 40个变量,这样一来输入、
输出、计算都会变得繁琐。
在这种情况下,我们希望能有一种数据类型可以保存一组数据,
并且可以方便的对这组数据进行输入、输出、计算等操作,因此引出了数组类型。
说明一个含有 40个元素的数组,
每个数组元素存放一个成绩,成绩的输入、输出、计算都可以通过循环来实现例 4-1,求某班成绩的平均分,并输出所有学生的成绩
#include <stdio.h>
void main( )
{ int j,sum=0,s[40] ;
float ave ;
for(j=0; j<=39 ; j++)
{ scanf(“%d”,&s[j]);
sum=sum+s[j];
}
ave=sum/40;
printf(“ave=%f”,ave);
for(j=0; j<40 ; j++)
printf(“%d,”,s[j]);
}
4.1.1 一维数组的引出及使用二、数组的概念
1,数组,由具有相同类型的固定数量的元素组成的集合
2,数组元素,每一个数组元素都是一个变量,为了与一般的变量相区别,我们称数组元素为 下标变量
3.下标变量在数组中的位置序号称 下标下标变量的数据类型称为 下标类型 (或元素类型 )
4.1.1 一维数组的引出及使用三、一维数组的定义
1,格式,类型标识符 数组名 [ 常量表达式 ] ;
例,int a[10] ;
2,说明
(1) 数组的类型实际上是指数组元素的取值类型。 对于同一个数组,所有元素的数据类型都是相同的。
84
:
66
80
951010
1012
1014
:
1028
a[0]
a[1]
a[2]
:
a[9](2) 数组名是用户定义的标识符,
数组名表示了一个存储区的首地址
(即第一个数组元素的地址 )
例,一个变量 x的地址可以用 &x来表示一个数组 a的地址就用数组名 a来表示,a等价于 &a[0]
4.1.1 一维数组的引出及使用
2,说明
(3) 数组长度,指数组中元素的个数
(4) 数组元素的 下标由零开始例,int a[10] 中 a 有 10个元素,所以数组长度为 10,
数组元素分别是,a[0],a[1] … a[8],a[9]
(5) 常量表达式中不能包含变量,其值也不能是实数
int n;
scanf("%d",&n);
int a[n];
int n=6;
int a[n];
int b[8.5];
#define SIZE 8

int a[2+3];
float b[SIZE];
对错
4.1.1 一维数组的引出及使用四、数组元素的引用
1,引用形式,数组名 [ 下标 ]
注意,
如果出现 a[5] = 72 ; 编译时不会指出错误,系统会将 a[4]后下一个存储单元 赋值为 72,但这样可能会破坏数组以外其他变量的值
84
75
66
80
951010
1012
1014
1016
1018
a[0]
a[1]
a[2]
a[3]
a[4]
72 a[5]1020
假设这个存储空间是变量 x
的,实际上 a[5]是不存在的,
如果执行了 a[5]=72,会将 x
原有的正确数据覆盖掉
2,说明
(1) 下标可以是整型常量或整型表达式如,a[1],a[2*3]
(2) 数组定义为 int a[5],数组长度为 5
而下标在 0 -- 4之内,即 a[0] -- a[4]
4.1.1 一维数组的引出及使用五,一维数组的初始化
1,概念,在定义一维数组时对各元素指定初始值称为数组的初始化 int a[5] = { 1,3,5,7,9 } ;
2,说明
(1) 对数组的全体元素指定初值,初值用 { } 括起来,数据之间用逗号分开。这种情况下,可以不指明数组的长度,系统会根据
{ }内数据的个数确定数组的长度
int a[ ] = { 1,3,5,7,9 } ;
(2) 对数组中部分元素指定初值 ( 这时不能省略数组长度 )
int a[5] = { 1,3,5 };
(3) 使数组中的全部元素初始值都为 0
int a[5] = { 0,0,0,0,0 } ; 简单的写法,int a[5]={ 0 } ;
4.1.1 一维数组的引出及使用
1 3 5 7 9
1 3 5 0 0
逆序
#include<stdio.h>
#define N 10
void main( )
{ int i,t,a[N];
for ( i=0; i<N; i++)
a[i] = i ;
for(i=0; i<N/2; i++)
{ t=a[i];
a[i]=a[N-1-i];
a[N-1-i]=t;
}
for( i=0; i<=9; i++)
printf("%3d ",a[i]);
}
例 4-2,使数组元素 a[0]~ a[9]的值为 0~ 9,将其逆序再输出
4.1.1 一维数组的引出及使用
5
8
0
1
9
2
6
3
7
4
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]
a[8]
a[9]
a[0]a[9]
4
5
7
8
3
0
a[1] a[8]a[2]a[7]
例 4-3,用数组求 fibonacci数列的前 20个数
#include <stdio.h>
void main( )
{ int i,f[20]={1,1};
for ( i=2 ; i<20 ; i++)
f[i]=f[i-2]+f[i-1];
for ( i=0; i<20 ; i++)
{ if ( i%4==0 )
printf(“\n”);
printf(“%6d”,f[i] );
}
}
1
1
0
0
0
0
:
0
f[0]
f[1]
f[2]
f[3]
f[4]
f[5]
:
f[19]
2
3
5
8
6765
i=2
f[2]=f[0]+f[1]
i=3
f[3]=f[1]+f[2]
i=4
f[4]=f[2]+f[3]
4.1.1 一维数组的引出及使用
4.1.1 一维数组的引出及使用例 4-4,用筛选法求 1—100间的素数
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 …
0 0 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 …
0 0 2 3 0 5 0 7 0 9 0 11 0 13 0 15 0 17 0 …
0 0 2 3 0 5 0 7 0 0 0 11 0 13 0 0 0 17 0 …
第 1步,先将 1筛掉第 2步,将 2的倍数都筛掉第 3步,将 3的倍数都筛掉逐步将 5… 11的倍数都筛掉,最后数组中的非零数就是素数
#include<stdio.h>
#include<math.h>
void main( )
{ int i,j,n,a[101];
for(i=0; i<=100; i++)
a[i]=i;
a[1]=0;
for( i=2; i<sqrt(100); i++)
for(j=i+1; j<=100; j++)
{ if(a[i]!=0&&a[j]!=0)
if(a[j]%a[i]==0)
a[j]=0;
}
… (见下页 )
4.1.1 一维数组的引出及使用
第 1步,将 1筛掉
每次循环,筛掉 i 的倍数
j 从 i+1开始逐渐变化到 100,
依次判断 j是否是 i 的倍数
对数组 a的每个元素赋值,
其值恰好等于其下标值
#include<stdio.h>
#include<math.h>
void main( )
{ …
n=0;
for(i=1;i<=100;i++)
{ if(a[i]!=0)
{ printf("%4d",a[i]);
n++;
}
if(n%10==0) printf("\n");
}
}
n是计数器,统计素数的个数
i 从 1变化到 100,
判断数组元素 a[i] 是否等于 0
每输出 10个换行
4.1.1 一维数组的引出及使用找到 x后结束循环例 4-5,输入一个数据,在已知数组中查找是否有该数据
5
8
0
1
9
2
6
3
7
4
x 9 a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]
a[8]
a[9]
#include <stdio.h>
void main( )
{ int i,x ;
int a[10]={ 5,8,0,1,9,2,6,3,7,4 };
scanf(“%d”,&x);
for ( i=0 ; i<10 ; i++)
if ( x= =a[i] )
{ printf(“find! a[%d]=x\n”,i);
break;
}
if ( i= =10 )
printf(“no find!\n”);
}
4.1.1 一维数组的引出及使用例 4-6,用冒泡排序法对 6个数进行排序 (从小到大 )
9
7
2
5
4
1
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
7
2
5
4
1
9
2
7
7
5
4
71
2
5
4
1
7
9
4
51
5
2
4
1
5
7
9
2
1
4
5
7
9
1
4
1
2
冒泡排序方法,依次比较相邻的两个数,将小数放前面,
大数放后面,n个数排序需要进行 n-1轮比较,从第 1轮到第 n-1轮,各轮的比较次数依次为,n-1次,n-2次 … 1次
9
7
2
5
4
19
9
9
9
9
7
2
5
4
1
初始状态 第 1轮 第 2轮 第 3轮 第 4轮 第 5轮
7
4.1.1 一维数组的引出及使用
#include <stdio.h>
#define N 6
void main( )
{ int a[N],i,j,t;
for ( i=0 ; i<N ; i++)
scanf(“%d”,&a[i] );
for ( i=0 ; i<N-1 ; i++)
for ( j=0 ; j<N-1-i ; 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(,%3d”,a[i] );
}
输入 6个数据用嵌套的 for循环实现排序外层循环控制进行几轮比较内层循环控制每一轮的比较次数若前面的数大于后面的数,则进行交换输出排序后的 6个数据
4.1.1 一维数组的引出及使用例 4-7,用选择排序法对 6个数进行排序 (从小到大 )
9
7
2
5
4
1
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
选择排序方法,第 1轮比较时,用 a[0]依次与 a[1]到 a[5]
进行比较,如果 a[0]较大则进行交换,第 1轮结束后,a[0]
中为最小数,以后各轮比较过程与第 1论类似,
1
9
7
5
4
2
1
2
9
7
5
4
7
9
5
7
4
5
2
4
1
2
4
9
7
5
1
2
4
5
9
7
7
9
5
7
4
5
9
7
2
5
4
1
72
9
7
1
2
初始状态 第 1轮 第 2轮 第 3轮 第 4轮 第 5轮
7
9
5
7
7
9
4.1.1 一维数组的引出及使用
#include <stdio.h>
#define N 6
void main( )
{ int a[N],i,j,t;
for ( i=0 ; i<N ; i++)
scanf(“%d”,&a[i] );
for ( i=0 ; i<N-1 ; i++)
for ( j=i+1 ; j<N ; j++)
if ( a[i]>a[j] )
{ t=a[i] ;
a[i]=a[j] ;
a[j]=t ;
}
for ( i=0 ; i<N ; i++)
printf(,%3d”,a[i] );
}
i=0 时,进行 5次比较,a[0]与 a[1]比,
a[0]与 a[2]比,…… a[0] 与 a[5]比,
最后 a[0]中为最小数
i=1 时,进行 4次比较,a[1]与 a[2]比,
a[1]与 a[3]比,…… a[1] 与 a[5]比,
最后 a[1]中为第 2小的数
i=2 时,进行 3次比较,a[2]与 a[3]比,
a[2]与 a[4]比,a[2]与 a[5]比,最后
a[2]中为第 3小的数
4.1.1 一维数组的引出及使用
冒泡排序的改进方法
9
7
1
2
4
5
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
7
1
2
4
5
9
1
2
4
5
7
9
1
2
4
5
7
9
1
2
4
5
7
9
第 1轮 第 2轮 第 3轮 第 4轮 第 5轮
1
2
4
5
7
9
从这道例题中我们发现,在进行完第二轮后,数据就排好序了,
在第三轮中数据没有进行一次交换,说明排序已经完成了,
第四、五轮的比较都是多余的,这种情况下应该终止排序过程初始状态为了解决问题,在程序中设置一个变量 flag,用它记录在每一轮比较中是否进行了交换在每轮比较开始前 flag=0,如果在此轮比较中进行了交换,
则 flag=1,在一轮比较结束后,
判断 flag的值是否为 1,如果 值为 1,则继续进行排序 ; 如果值为 0,说明在此轮比较中没有进行交换 (即已经完成排序了 ),
此时可终止循环 (即结束排序 )
#include <stdio.h>
void main( )
{ int a[6],i,j,t,flag;
for ( i=0; i<6; i++)
scanf(“%d”,&a[i] );
i=0 ;
do
{ flag=0;
for ( j=0 ; j<5-i ; j++)
if ( a[j]>a[j+1] )
{ t=a[j] ; a[j]=a[j+1] ;
a[j+1]=t ; flag=1;
}
i++ ;
} while ( flag ) ;
for ( i=0 ; i<6 ; i++)
printf(,%3d”,a[i] );
}
冒泡排序的改进方法
分析选择排序过程发现,在每一轮的比较中交换次数太多,我们可以尽量减少交换次数,实际上每轮比较只要一次进行交换就能完成排序,
对选择排序进行改进,其方法如下,
先从要排序的 n个数中找出最小的数,把它放在第一个位置
再从剩下的 n-1个数中找出最小的数,把它放在第二个位置
这样重复做下去
改进后的选择排序方法数据进行比较的次数并没有减少,但每一轮只进行一次交换,加快了程序运行速度
选择排序的改进方法
#include <stdio.h>
void main( )
{ int a[6],i,j,k,t;
for ( i=0 ; i<6 ; i++)
scanf(“%d”,&a[i] );
for ( i=0 ; i<5 ; i++)
{ k=i ;
for ( j=i+1 ; j<6 ; j++)
if ( a[k]>a[j] ) k=j ;
if ( k!=i )
{ t=a[i] ; a[i]=a[k] ;
a[k]=t ; } //end if
} // end for
for ( i=0 ; i<6 ; i++)
printf(,%3d”,a[i] );
}
9
7
2
5
4
1
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
0k 12
1
9
1
7
2
5
4
9
5!=0
a[0]和 a[5]换
5 k 12
2
7
2!=1
a[1]和 a[2]换
1
2
7
5
4
9
4!=2
a[2]和 a[4]换
k 234
4
7
设置变量 k用以存储当前最小数的下标
选择排序的改进方法
4.2.1 二维数组的引出
1、概念,一个一维数组,它的每一个元素都是类型相同的一维数组,就形成一个二维数组
2、定义形式,
类型标识符 数组名 [ 常量表达式 1 ] [ 常量表达式 2 ]
如,int a[3][4] ;
a[0]
a[1]
a[2]
a[0][0] a[0][1] a[0][2] a[0][3]
4.2 二维数组的引出及使用
3、存储形式,数组的元素在内存中是连续存放的
int a[3][3] ; 数组 a的存放形式如下,
a[1][0]?a[1][1]?a[1][2]
a[2][0]?a[2][1]?a[2][2]
a[0][0]?a[0][1]?a[0][2] a[0][0]
a[0][1]
a[0][2]
a[1][0]
a[1][1]
a[1][2]
a[2][0]
a[2][1]
a[2][2]
1010
1012
1014
1016
1018
1020
1022
1024
1026
二,二维数组的引用数组元素的表示形式,
数组名 [下标 1] [下标 2]
注意,(1) 每个下标都要用 [ ] 括起来如 a [2] [1] 不能写成 a [2,1]
(2) 下标不要超过定义的范围
4.2 二维数组的引出及使用
1 2 3 4
5 6 7 8
9 10 11 12
三,二维数组的初始化
1、分行初始化
int a[3][4]={{ 1,2,3,4 },{ 5,6,7,8 },{ 9,10,11,12 }};
第 1对 { }内的数据赋给第 1行数组元素,
以此类推,此方法较直观
2、按数据的排列顺序对数组元素赋初值
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
将数据依次赋给元素 a[0][0],a[0][1] …a[2][3]
注意,此方法数据没有明显的界限,当数据较多时容易出错
4,2 二维数组的引出及使用
3、对数组的部分元素赋初值
int a[3][4]={{1,2},{3},{0,4}}; int a[3][4]={1,2,3,4,5,6};
1 2 0 0
3 0 0 0
0 4 0 0
1 2 3 4
5 6 0 0
0 0 0 0
4、对数组的全部元素赋初值时可以省略第一维的长度系统会根据数据的个数和第二维的长度自动求出第一维的长度数组 a第一维长度为 3
数组 b第一维长度为 4
int a[ ][4]={ {1,2},{ 0,3,4 },{ 5 } } ;
int b[ ][2]={ 1,2,3,4,5,6,7,8 } ;
4.1.2 二维数组的引出及使用
#include <stdio.h>
void main( )
{ int i,j,row=0,col=0,max ;
int a[3][4]={{5,2,0,9},{3,7,12,6},{10,4,1,8}};
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 ; col=j ;
}
printf(“max=%d\n”,max);
printf(“max=a[%d][%d]\n”,row,col);
}
例 4-8,找出矩阵中最大的数,并输出其行号和列号
max
0row
0col
59
3
12
1
2
输出,
max=12
max=a[1][2]
5 2 0 9
3 7 12 6
10 4 1 8
4.1.2 二维数组的引出及使用
#include<stdio.h>
void main( )
{ int a[3][4],b[4][3],i,j ;
for ( i=0 ; i<3 ; i++ )
for ( j=0 ; j<4 ; j++ )
scanf(“%d”,&a[i][j] ) ;
3
7
12
6
10
4
1
8
5
2
0
9
例 4-9,将一个矩阵进行转置 (即原来的行变为列 )
5 2 0 9
3 7 12 6
10 4 1 8
输入数组 a
进行矩阵转置
a[0][
2]
b[2][
0]
a[2][
1]
b[1][
2]
输出数组 b
for ( i=0 ; i<3 ; i++ )
for (j=0 ; j<4 ; j++)
b[j][i]=a[i][j];
for ( i=0 ; i<4 ; i++ )
{ for ( j=0 ; j<3 ; j++ )
printf(“%5d”,b[i][j]) ;
printf(“\n”);
}
}
4.1.2 二维数组的引出及使用例 4-10,一个学习小组有 5个人,每个人有三门课的考试成绩,
求每个人的总分和该学习小组各门课的平均成绩。
姓名 高数 英语 C语言 总分
Mary 85 78 88?
John 90 80 91?
Mike 79 92 84?
Alex 83 86 80?
Karry 87 75 95?
用一个二维数组来存放成绩
int s[5][4];
用一个一维数组来存放 各门课的平均 成绩
float a[3];每个人三门课的成绩是输入的总分和各门课的平均成绩是计算出来的
4.1.2 二维数组的引出及使用
#include<stdio.h>
void main( )
{ int i,j,s[5][4],sum; float a[3];
for(i=0; i<5; i++)
{ for(j=0; j<3; j++)
scanf("%d",&s[i][j]);
s[i][j]=s[i][0]+s[i][1]+s[i][2];
}
for(j=0; j<3; j++)
{ sum=0; //注意赋初值的位置
for(i=0; i<5; i++)
sum=sum+s[i][j];
a[j]=sum/5.0;
}
… (见下页 )
输入一个学生 3
门课的成绩计算总分
4.1.2 二维数组的引出及使用对每一门课 (即每一列 )
进行处理,先将每列的
5个成绩累加求和 ; 再将总和除 5,得到每门课的平均分数组 a保存每门课的平均分二维数组 s存放每个学生的成绩
#include<stdio.h>
void main( )
{ …
for(i=0; i<5; i++)
{ for(j=0; j<4; j++)
printf("%4d",s[i][j]);
printf(" \n ");
}
for(i=0; i<3; i++)
printf(" %6.2f ",a[i]);
printf(" \n ");
}
控制输出 5行
4.1.2 二维数组的引出及使用控制每行输出 4个成绩输出一行的 4个成绩后再换行输出结果,
_ _85_ _78_ _88_ 251
_ _90_ _80_ _91_ 261
_ _79_ _92_ _84_ 255
_ _83_ _86_ _80_ 249
_ _87_ _75_ _95_ 257
_84.80_82.20_87.60
4.3 字符数组一,字符数组和字符串
1,字符数组定义,char c[10];
2,字符串,由若干个有效字符组成的序列有效字符包括字母,数字,专用字符,转义字符如,,bfer”,a45-7”,m\tk\n”
3,字符串的存储
C语言中没有专门的字符串变量,因此字符串存放在字符数组中,字符串以,\0”作为结束标志例,char c[5] ;
① c[0]=?O? ; c[1]=?K? ; c[2]=?!? ; O K !
O K ! \0② c[0]=?O?; c[1]=?K?; c[2]=?!?; c[3]=?\0? ;
注意,字符数组与字符串并不相同二,字符数组的初始化
1,为数组中的元素指定初值
char c[8] = {?H?,?e?,?l?,?l?,?o? } ;
如果对数组全部元素指定初值,则可省略数组的长度
char c[ ] = {?H?,?e?,?l?,?l?,?o? } ;
H e l l o \0 \0 \0
H e l l o
2,对字符数组指定字符串初值
char c[8] = {,Hello” } ;
char c[8] =,Hello” ;
char c[8] = {?H?,?e?,?l?,?l?,?o?,?\0? } ;
三种形式等价
char c[ ] =,Hello”; char c[6]=“Hello”;
char c[5] =,Hello” ;
H e l l o \0
H e l l o \0
注意,初始化时数组长度应足够大,确保可以容纳所有字符和
‘ \0’
4.3 字符数组错误的初始化三,字符串的输入
1,输入单个字符
for ( i=0 ; i<8 ; i++ )
scanf(“%c”,&c[i] ) ;
2,输入一个字符串,scanf (,%s”,c ) ;
说明,用格式字符 %s 输入字符串时,字符数组变量 c不必加地址符 &,因为 数组名本身代表数组的首地址注意,(1) 从键盘输入字符串时不加,”
(2) 用 scanf 输入字符串时,空格和回车符都会作为字符串的分隔符,即 scanf不能用来输入包含有空格的字符串
char c[8] ; int i ;
scanf(“%c%c%c”,&c[0],&c[1],&c[2]) ;
4.3 字符数组
3,字符串输入函数 gets ( 字符数组 )
如,gets(c) ; 说明,输入有空格的字符串时应使用函数 gets,
它会读入全部字符直到遇到回车符为止例,int i ;
char c[8];
① for ( i=0 ; i<8 ; i++ )
scanf(“%c”,&c[i]) ;
② scanf(“%s”,c ) ;
③ gets ( c ) ;
假设输入为,abc def↙
a b c d e f ↙
a b c \0
a b c d e f \0
4.3 字符数组四,字符串的输出
1,输出单个字符
char c[8] ; int i ;
printf(,%c%c”,c[0],c]1] ) ;
for ( i=0 ; i<8 ; i++ )
printf(,%c”,c[i] ) ;
2,输出字符串 printf(,%s”,c ) ;
注意,
输出时不包括字符‘ \0’,如果一个字符串中有多个
‘ \0’,
则输出时遇到第一个‘ \0’即认为字符串结束3,字符串输出函数 puts ( 字符数组 )
一次输出一个字符串,输出时将‘ \0’自动转换成换行符
4.3 字符数组例,char s1[5]=“abc”,s2[10]=“defg” ;
printf(,%s%s\n”,s1,s2 ) ;
puts( s1 ) ;
puts( s2 ) ;
输出,
abcdefg
abc
defg
例,int i ;
char c[8];
gets ( c ) ;
for ( i=0 ; i<8 ; i++ )
printf(“%c”,c[i] );
printf(“\n”)
puts ( c );
假设输入为,abc defgh↙
a b c d e f g h \0
输出,
abc defg
abc defgh
注意,用 gets输入字符串时,若输入字符数目大于字符数组的长度,多出的字符则会存放在数组的合法存储空间之外
4.3 字符数组存在隐患错五、字符串处理函数 (用字符串函数时要写 #include <string.h>)
1,字符串拷贝函数注意,C语言不允许用赋值表达式对字符数组赋值
char s1[5]=“abc”,s2[3],s3[8] ;
s2 =,abc” ;
s3 = s1 ;
// 赋值与初始化不同
// 对 s2,s3 的赋值都是非法的希望 s2或 s3中也存放字符串,abc”要用字符串拷贝函数? 格式,strcpy( 字符数组变量 1,字符串 2 )
作用,将字符串 2中的字符复制到字符数组 1中注意,① 字符数组 1 必须足够大
② 字符串 2可以是字符串常量,或是字符数组变量
③ 拷贝时‘ \0’也一起拷贝
4.3 字符数组例,char s1[5]=“abc”,s2[3],s3[8] ;
strcpy ( s3,s1 ) ;
strcpy ( s2,s1 ) ;
a b c \0
a b c \0s1
s3
a b c \0s2
函数 strncpy( 字符数组 1,字符串 2,n )
作用,将字符串 2的前 n个字符复制到字符数组 1 中例,char c1[10]=“program”,c2[ ]=“abcdef” ;
strncpy( c1,c2,3 ) ;
p r o g r a m \0c1
a b c d e f \0c2
a b c
4.3 字符数组存在隐患
2,字符串连接函数
格式,strcat ( 字符数组变量 1,字符串 2 )
作用,将字符串 2中的字符连接到字符串 1 的后面,
产生的新字符串仍存放在字符数组 1 中
说明,连接时将字符串 1 末尾的‘ \0’将去掉,而在连接后的新字符串末尾添加‘ \0’
注意,字符数组 1要足够大例,char s1[10]=“abc”,s2[ ]=“def” ;
strcat ( s1,s2 ) ;
strcat ( s1,“gh” ) ;
a b c \0
d e f \0
s1
s2
d e f \0g h \0
4.3 字符数组
3,字符串比较函数
格式,strcmp ( 字符串 1,字符串 2 )
作用,比较两个字符串的大小
说明,
① 两个字符串可能是字符串常量或字符数组变量
② 两个字符串比较时,从字符串中的第一个字符开始逐个比较其 ASCII码值,直到出现不同字符或出现‘ \0’为止
③ 比较的结果由函数值带回
str1 = = str2 函数值为 0
str1 > str2 函数值为正数
str1 < str2 函数值为负数
4.3 字符数组例,int n1,n2,n3 ;
char s1[5],s2[5] ;
n1 = strcmp (,abc”,“def” ) ;
strcpy( s1,“dfg” ) ;
strcpy( s2,“you” ) ;
n2 = strcmp ( s1,“def” ) ;
n3 = strcmp ( s1,s2 ) ;
字符‘ a?与‘ d?
比较
n1= -3 ( 97-100 )
先‘ d?与‘ d?比较,相同,
然后‘ f?与‘ e?比较
n2=1 ( 102-101)
字符‘ d?与‘ y?比较
n3= -21(100-121)
d f g \0
y o u \0
s1
s2
4.3 字符数组
4,测字符串长度函数
格式,strlen (字符串 )
作用,测出字符串中实际字符的个数 ( 不包括‘ \0’ )
例,int len1,len2 ;
char s[10] ;
len1 = strlen(,computer”) ;
gets(s) ;
len2 = strlen(s) ;
5,字符串中大、小字母转换
strlwr (字符串 ) 将字符串中大写字母换成小写字母
strupr (字符串 ) 将字符串中小写字母换成大写字母
4.3 字符数组
4.3 字符数组例 4-11 输入一行字符,统计其中有多少个单词
T h i s i s a p r o g r a m
具体方法,设置一个标志变量 word,如果当前字符是空格,则 word=0; 如果如果当前字符不是空格,则 word=1
分析,
用一个字符数组来保存一行字符因为单词是由空格分开的,所以统计单词个数关键在于判断某个字符是否为空格
0word 1
#include <stdio.h>
void main( )
{ char string[81];
int i,num=0,word=0;
char c;
gets(string);
for(i=0; (c=string[i])!= '\0'; i++)
if (c==' ') word=0;
else
if (word==0)
{ word=1;
num++; }
printf (“There are %d words.\n”,num);
}
4.3 字符数组
num用来统计单词个数,word
是判别是否为单词的标志,若
word=0表示未出现单词,如出现单词 word就置成 1
c不是空格,若 c前面的字符是空格,表明这是一个新单词的开始,
则 word=1,num加 1; 若 c前面的字符不是空格,则不作任何处理
4.3 字符数组例 4-12 编程实现两个字符串的连接,但不能使用 strcat函数
a b c d \0
x y z \0
x y 7 \0
s2
s1
目标,将字符串 s2 连接到字符串 s1后面步骤 1:让数组 s1 的下标指向字符串的末尾,即‘ \0’的位置步骤 2:依次将数组 s2 的字符赋给 s1,它们的下标都加 1
步骤 3:最后数组 s1 的末尾赋值为‘ \0’
#include<stdio.h>
void main( )
{ char s1[80],s2[40];
int i=0,j=0;
printf(“Input string1:”); gets(s1);
printf(“Input string2:”); gets(s2);
while(s1[i]!='\0')
i++;
while(s2[j]!='\0')
s1[i++]=s2[j++];
s1[i]='\0';
printf(“The new string is,%s”,s1);
}
4.3 字符数组
i 和 j分别是 s1 和 s2的下标
当元素 i不是‘ \0’ 时,
让 i加 1,指向下个元素
依次将 s2的元素 j赋给 s1的元素 i
s1[i]=s2[j];
i++; j++;?S1末尾赋
‘ \0’
4.4 数组与函数一、数组元素作函数参数其用法与变量作实在参数一样,是单向传递二、数组名作函数参数 (实参和形参都使用数组名 )
说明,
1,必须在主调函数和被调函数中分别定义数组
2,实参数组和形参数组的类型必须一致
3,数组名作参数就是将实参数组的 首地址 传给形参数组即实参数组和形参数组占用同一片内存单元
4,定义形参数组时可以不指定其大小,
当形参数组发生变化时,实参数组也随之发生变化
4.4 数组与函数例 4-13:求某班成绩的平均分
#include <stdio.h>
float average(int a[40]);
void main( )
{ int i,s[40] ;
float aver ;
for(i=0; i<=39 ; i++)
scanf(“%d”,&s[i]);
aver=average(s);
printf(“aver=%f\n”,aver);
}
float average(int a[40])
{ int i ;
float sum,ave ;
sum=0;
for(i=0; i<=39 ; i++)
sum=sum+a[i];
ave=sum/40;
return(ave);
}
4.4 数组与函数数组占用存储空间
s[0]
s[1]
s[2]
:
:
:
:
s[39]
main average
a[0]
a[1]
a[2]
:
:
:
:
a[39]
main 调用 average
sc?s
输入 sc的
40个元素
aver=average(sc)
sum=0
计算 sum
输出 aver
返回 ave的值结束
ave=sum/40
78
84
90
:
:
:
:
66
4.4 数组与函数例 4-14,用函数实现冒泡排序,并用数组名作参数
#include <stdio.h>
void sort (int a[ ],int n);
void main( )
{ int b[100],i,m ;
scanf(“%d”,&m ) ;
for ( i=0 ; i<m ; i++)
scanf(“%d”,&b[i] ) ;
sort( b,m );
for ( i=0 ; i<m ; i++)
printf(“%3d”,b[i] ) ;
}
void sort( int a[ ],int n)
{ int i,j,t ;
for ( i=0; i<n-1; i++)
for ( j=0; j<n-1-i; j++)
if ( a[j]>a[j+1 ] )
{ t=a[j] ;
a[j]=a[j+1];
a[j+1]=t;
}
}说明:将数组 b定义的较大,
每次需要排序的数的个数可以不同
4.5 数组与指针一,一维数组与指针
1,一维数组及元素的地址表示
int a[5] = { 1,2,3,4,5 } ; 数组的地址,a
元 素 地 址
*a a[0] &a[0] a
*(a+1) a[1] &a[1] a+1
*(a+2) a[2] &a[2] a+2
*(a+3) a[3] &a[3] a+3
*(a+4) a[4] &a[4] a+4
4.5 数组与指针
2,用指针变量引用数组元素
(1) 定义指针变量
int *p,a[5] = { 1,2,3,4,5 } ;
p = a ;
地址 元素 地址 元素 地址 元素
&a[0] a[0] a *a p *p
&a[1] a[1] a+1 *(a+1) p+1 *(p+1)
&a[2] a[2] a+2 *(a+2) p+2 *(p+2)
&a[3] a[3] a+3 *(a+3) p+3 *(p+3)
&a[4] a[4] a+4 *(a+4) p+4 *(p+4)
4.5 数组与指针
(2) 引用数组元素下标法 地址法 指针法第 k个元素 a[k] *(a+k) *(p+k)
第 k个元素的地址 &a[k] a+k p+k
注意,指针变量也可以加下标,即,p[k] 等价于 a[k]
① 分别用三种方法输出数组元素,其效率不同,
下标法与地址法的效率相同,指针法的效率较快
② 用指针变量访问数组元素时要注意下标是否越界
4.5 数组与指针
#include <stdio.h>
void main( )
{ int a[5]={1,3,5,7,9 },*p,i ;
for( i=0; i<5; i++) printf(“%3d”,a[i]);
printf(“\n”) ;
for(i=0; i<5; i++) printf(“%3d”,*(a+i));
printf(“\n”) ;
for (p=a; p<a+5; p++) printf(“%3d”,*p);
printf(“\n”) ;
}
例:用三种方法输出数组元素注意:可以用 p++,但不能用 a++,因为 a
代表数组的首地址,它是地址常量,不能改变,而 p 是一个指针变量,可以改变
4.5 数组与指针例 4-15,将数组 a中全部元素加 1,再输出 a
#include <stdio.h>
void main( )
{ int a[5] = {1,3,5,7,9 },*p,j ;
for ( p=a ; p<a+5 ; p++ )
printf(“%3d”,*p);
printf(“\n”) ;
for ( j=0 ; j<5 ; j++)
a[j]=a[j]+1 ;
for ( j=0 ; j<5 ; j++)
printf(“%3d”,*(p+j) ) ;
printf(“\n”) ;
}
p=a ;
1
3
5
7
9
a
a+1
a+2
a+3
a+4
2
4
6
8
10
p
使用指针变量要注意它的当前值
4.5 数组与指针
3,指向数组的指针变量作函数参数例 4-16 ① 实参和形参都用数组名
#include <stdio.h>
void inv1( int x[ ],int n )
{ int temp,i,j,m=(n-1)/2;
for( i=0 ; i<=m; i++)
{ j=n-1-i ; temp=x[i] ;
x[i]=x[j]; x[j]=temp ; }
}
void main( )
{ int i,a[6]={ 1,3,4,6,7,9 };
inv1(a,6 );
for( i=0; i<6; i++ ) printf(“%3d”,a[i] );
printf(“\n”);
}
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
x[0]
x[1]
x[2]
x[3]
x[4]
x[5]
1
3
4
6
7
9
main inv1
2m
0i
5j
9
1
1
4
7
3
6
4
2
3
4.5 数组与指针例 4-16 ②实参用数组名,形参用指针变量
#include <stdio.h>
void inv2(int *x,int n)
{ int temp,m=(n-1)/2;
int *p,*i,*j ;
i=x ; j=x+n-1; p=x+m;
for( ; i<=p ; i++,j-- )
{ temp=*i ; *i=*j ; *j=temp ; }
}
void main( )
{ int i,a[6]={ 1,3,4,6,7,9 };
inv2(a,6 );
for( i=0; i<6; i++ ) printf(“%3d”,a[i] );
printf(“\n”);
}
1
3
4
6
7
9
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
ax
ai
6n
a+5j
a+2p
inv2
9
1
7
3
6
4
2m
temp 1
a+1
4
2
3
34
3
2
例 4-16 ③实参和形参都用指针变量
#include <stdio.h>
void inv3(int *x,int n);
void main( )
{ int *p,a[6]={1,3,4,6,7,9};
p = a ; inv3( p,6 );
for( p=a ; p<a+6 ; p++ ) printf(“%3d”,*p );
printf(“\n”);
}
void inv3(int *x,int n)
{ int *p,*i,*j,temp,m=(n-1)/2;
i=x ; j=x+n-1; p=x+m;
for( ; i<=p ; i++,j-- )
{ temp=*i ; *i=*j ; *j=temp ; }
}
4.5 数组与指针例 4-16 ④实参用指针变量,形参用数组名
#include <stdio.h>
void inv4(int x[ ],int n);
void main( )
{ int *p,a[6]={1,3,4,6,7,9};
p = a ; inv4( p,6 ) ;
for( p=a ; p<a+6 ; p++ ) printf(“%3d”,*p );
printf(“\n”);
}
void inv4(int x[ ],int n)
{ int temp,i,j,m=(n-1)/2;
for( i=0 ; i<=m ; i++)
{ j=n-1-i ; temp=x[i] ;
x[i]=x[j] ; x[j]=temp; }
}
4.5 数组与指针
4.5 数组与指针例 4-17 求数组中最大和最小元素
#include <stdio.h>
int max,min ;
void m1(int arr[ ],int n)
{ int *p,*end ;
end=arr+n ; max=min=*arr ;
for(p=arr+1 ; p<end ; p++)
if( *p>max ) max=*p ;
else if( *p<min ) min=*p ;
}
void main( )
{ int i,a[6];
for(i=0 ; i<6 ; i++) scanf(“%d”,&a[i] );
m1( a,6 );
printf(“max=%d,min=%d\n”,max,min);
}
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
6n
arr+1p
arr+6end
max
min
4
7
1
9
0
5
aarr
4
4
79
10
4.6 字符串与指针一、字符指针
1,定义指向字符串的指针变量
char *p =,China”;
注意,赋值只是把字符串的首地址赋给 p,
而不是把字符串赋给 p,p 是一个指针变量,
它不能存放一个字符串,只能存放一个地址
C
h
i
n
a
\0
2460
2461
2462
2463
2464
2465
2460p
将字符串的首地址赋给 p
说明,这里没有定义字符数组,但字符串在内存中还是以数组形式存放的字符串在内存中占有一片连续的存储单元,
以‘ \0’结束
2,输出字符串,
char *p =,China”;
printf(“%s\n”,p ) ;
输出字符串时,先输出 p 指向的第一个字符,然后系统自动执行 p++,使 p 指向下一个字符,再输出该字符,直到遇到 ‘ \0’
为止
C
h
i
n
a
\0
2460
2461
2462
2463
2464
2465
2460p 24612462246324642465
用循环逐个输出字符串中的字符,
char *p =,China”;
for ( ; *p!=?\0?; p++ )
printf(“%c”,*p ) ;
4.6 字符串与指针二,字符数组与字符指针变量的区别
1,存储方式不同,
字符数组在定义时,不论是否进行初始化,都会为其分配存储空间,用来存放字符串中的字符和‘ \0’
字符指针是分配一个指针变量的存储单元,用于存放地址。如果字符指针定义时没有进行初始化,则不会为其分配字符串的存储空间;如果定义时进行了初始化,则还要分配一块连续内存空间存储字符串,并将存储空间的起始地址赋给字符指针。
char s1[5],s2[8] =“Program”;
P r o g r a m \0
s1
s2
char *p1,*p2=“abc”;
4.6 字符串与指针
p1
p2
a
b
c
\0
2460
2461
2462
2463
2460
4.6 字符串与指针二,字符数组与字符指针变量的区别
2,数组名表示字符串的首地址,但数组名是一个地址常量,
它的值是不能变的,而指针变量的值是可以改变的
char *p =“Program”;
for ( ; *p!=?\0?; p++ )
printf(“%c”,*p ) ; 对
char s[10] =“Program” ;
for ( ; *s!=?\0?; s++ )
printf(“%c”,*s ) ; 错
3,赋值方式不同字符数组可以初始化,可以给每个元素赋值,但不能整体赋值字符指针可以初始化,也可以整体赋值
char s[8],str[ ]=,good” ; //对
s[0]=?c?; s[1]=?h?; //对
s[8] =,China” ; //错
s =,China” ; //错
char *p1;
char *p2=,abcd”;
p1=,China” ; 对
4.6 字符串与指针例 4-18 按字典排列法比较两个单词的大小
#include<stdio.h>
void main( )
{ char a[20],b[20]; int i=0;
gets(a); gets(b);
while((*(a+i) = = *(b+i))&&*(a+i)!= '\0')
i++;
if(*(a+i)= = '\0'&&*(b+i)= = '\0')
printf(“%s = %s\n”,a,b);
else
if(*(a+i)>*(b+i)) printf(“%s > %s\n”,a,b);
else
printf(“%s < %s\n”,a,b);
}
方法 1,用字符数组实现 s t u d y \0 …a
s t u d e n t \0 …b
0i 1234
#include<stdio.h>
void main( )
{ char a[20],b[20],*sa,*sb;
sa=a; sb=b;
gets(sa); gets(sb);
while((*sa = = *sb) && *sa != '\0')
{ sa++; sb++; }
if(*sa = = '\0'&&*sb = = '\0')
printf(“%s = %s\n”,sa,sb);
else
if(*sa>*sb) printf(“%s > %s\n”,sa,sb);
else
printf(“%s < %s\n”,sa,sb);
}
方法 2,用字符指针实现
4.6 字符串与指针三、字符串指针作函数参数例 4-19 实现字符串复制 ①
void copystr(char from[ ],char to[ ])
{ int i = 0 ;
while ( from[i]!=?\0? )
{ to[i] = from[i] ; i++; }
to[i] =?\0? ;
}
void mani( )
{ char a[ ] =,cat”,b[ ] =,tiger”;
puts(a); puts(b);
copystr(a,b);
puts(a); puts(b);
}
c
a
t
\0
t
i
g
e
r
\0
a b
from a to b c
a
t
\0
相当于 b[i]=a[i]
4.6 字符串与指针例 4-19 ②
void copystr(char *from,char *to)
{ for( ; *from!=?\0? ; from++,to++ )
*to = *from ;
*to =?\0? ;
}
void main( )
{ char *a =,cat”,*b =,tiger”;
puts(a); puts(b);
copystr(a,b);
puts(a); puts(b);
}
c
a
t
\0
t
i
g
e
r
\0
a b
from a to b c
a
t
\0
a+1 b+1+2 +2+3 +3
4.6 字符串与指针
4.7 典型例题例 4-20 折半查找
2 5 8 10 14 19 23 54 62 81
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
midbott top
前提条件:有一组数已经按从小到大 (或从大到小 )排序
目标:输入一个数 x,在这组数查找是否有 x
折半查找的步骤:
1、确定三个关键下标的初值,bott=0,top=9,mid=(bott+top)/2;
2,判断要找的数 x是否等于 a[mid]
① x==a[mid] 找到,结束
② x<a[mid] 在 a[bott]—a[mid-1]之间继续查找 x
top=mid-1; mid=(bott+top)/2;
③ x>a[mid] 在 a[mid+1]—a[top]之间继续查找 x
bott=mid+1; mid=(bott+top)/2;
2 5 8 10 14 19 23 54 62 81
a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9]
midbott top
设 x=5,查找过程如下:
bott=0,top=9,mid=(bott+top)/2=4;
因 x<a[4],所以 top=mid-1=3,mid=(bott+top)/2=1
因 x>a[4],所以 bott=mid+1=5,mid=(bott+top)/2=7
x==a[1],找到,结束
topmid
设 x=77,查找过程如下:
bott=0,top=9,mid=(bott+top)/2=4;
因 x>a[7],所以 bott=mid+1=8,mid=(bott+top)/2=8
因 x>a[8],所以 bott=mid+1=9,mid=(bott+top)/2=9
因 x<a[9],所以 top=mid-1=8,这时 bott>top不用再找了,结束
topbott mid bottmidbott
mid
top
#include<stdio.h>
#define N 10 // 符号常量定义,可以方便的修改数组的大小
void main( )
{ int i,bott,top,mid,loca,sign,x,a[N];
for(i=0;i<N;i++)
scanf(“%d”,&a[i]); // 输入数组 a
printf(“input x:”);
scanf(“%d”,&x); // 输入要查找的数 x
bott=0; top=N-1;
sign=1; // 标志变量设置初值,1表示没找到,0表示找到
if (x<a[0] || x>a[N-1]) // 若 x比 a[0]小,比 a[N-1]大,置 loca为 -1
loca= -1; // loca变量表示数 x在数组中的下标,
当数组中没有 x时,其值为 -1
例 4-20 程序代码
4.5 典型例题
while((sign = = 1)&&(bott<=top))
{ mid=(bott+top)/2; // 计算中间位置的下标
if (x = = a[mid]) // 若 x等于 a[mid],表示找到
{ loca=mid; // x等于 a[mid],用 loca 记下该下标
printf(“Find %d==a[%d]\n”,x,loca); // 输出结果
sign=0; //标志变量赋 0,从而结束循环
}
else // x不等于 a[mid]的情况
if(x<a[mid]) top=mid-1; // x小则查找数组中较小的一半
else bott=mid+1; // x大则查找数组中较大的一半
}
if (sign = =1 || loca= = -1) // 满足其中任何一个条件,表示没找到
printf(“%d is not found.\n”,x);
}
4.5 典型例题例 4-26 有 n个人围成一圈,顺序排号,从第一个人开始报数 (从 1报到 3),凡是报到 3的人退出圈子,问最后留下的人是原来的第几号
4.5 典型例题
1 2
3
4
5
6
7
8
910111213
14
15
16
17
18
19 20
1
2
3
4
5
6
7
8910
0
11
12
13
14
15
16
17
18 19
0
0
00
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
#include<stdio.h>
#define N 50
void main( )
{ int i,k,m,n,num[N],*p;
printf(,Input number of person,n=” );
scanf(“%d”,&n);
p=num; //令指针变量 p指向数组 num
for( i=0; i<n; i++)
*(p+i)=i+1; //编号,第 i个元素的编号为 i+1
i=0; // i为循环控制变量,每次循环由 0逐步变化至 n-1
k=0; // k为按 1,2,3报数时的计数器
m=0; // m为退出人数的计数器
4.5 典型例题例 4-26 程序代码
while (m<n-1) // 当退出人数小于 n-1人 (即剩余人数大于 1)时,执行循环
{ if(*(p+i)!=0) k++; // 没有退出的人进行报数
if(k = = 3) // 报到 3 的人退出
{ *(p+i)=0; // 退出的人编号置为 0
k=0; // k清 0,以便重新从 1开始报数
m++; // 退出的总人数增加 1
}
i++; // 轮到下一个人报数
if( i = = n) i=0; //报数到最后一个人后,i重置为 0
}
while (*p==0) p++; // 注意 p的初值是 p=num,p指向第 1个人
printf(“The last one is NO.%d\n”,*p);
}
4.5 典型例题例 4-26 程序代码