第六章 数组
目录
概述
数组:按序排列的同类数据元素的集合
C语言中,数组属于构造数据类型。一个数组可以分解为多个数组元素,这些数组元素可以是基本数据类型或构造数据类型。
按数组元素的类型划分,数组可分为数值数组,字符数组,指针数组,结构数组等类别。
本章介绍数值数组和字符数组。
6.1 一维数组
6.1.1 一维数组的定义
概念:只有一个下标的数组
格式:类型说明符 数组名[常量表达式];
其中:
类型说明符可以是任意一种基本数据类型或构造数据类型
数组名是用户定义的数组标识符
方括号中的常量表达式表示数据元素的个数,也称为数组的长度
说明:
1.数组的类型实际上是数组元素的取值类型。对于同一个数组,其所有元素的数据类型都是相同的
2.数组名的书写规则应符合标识符的命名规则
3.方括号中的常量表达式表示数组元素的个数
4.数组名不能与其它变量名相同,例如下列写法是错误的
void main()
{
int a;
float a[10];
......
}
5.编译程序为数组开辟连续的存储单元来顺序存放数组中各数组元素的值,用数组名表示该数组存储区的首地址
举例:
a[0] | a[1] | a[2] | a[3] | a[4] |
---|---|---|---|---|
100 | 104 | 108 | 112 | 116 |
&a[2] = a + 2 * 4; //100 + 8 = 108
6.方括号中数组元素个数的表示可以是常量、符号常数或常量表达式,但不能是变量。C 语言不允许动态定义数组的大小
7.允许在同一个类型说明中,说明多个数组和多个变量。例如:
int a,b,c,d,k1[10],k2[20];
6.1.2 一维数组的引用
一维数组元素的表示形式:
数组名[下标表达式]
- 数组元素的下标只能是整型常量或整型表达式
6.1.3 一维数组的初始化
对数组元素进行赋值:
1.用赋值语句逐一对数组元素赋值。
2.用循环语句配合 scanf 函数逐一对数组元素赋值
3.初始化赋值。
关于初始化赋值,其一般格式为:==类型说明符 数组名[常量表达式]={值 1,值 2……值 n};==例如:
int a[10] = {1,2,3,4,5,6,7,8,9,10};
说明:
1.初始化时可以只给部分元素赋初值。当{ }中值的个数少于元素个数时,只给前面部分元素赋值。例如:int a[10]={0,1,2,3,4};表示只给 a[0]~a[4]五个元素依次赋值,而后五个元素自动赋 0 值。
2.只能给元素逐一赋值,不能给数组整体赋值。例如给十个元素全部赋值 1,只能写为:int a[10]={1,1,1,1,1,1,1,1,1,1};而不能写为:int a[10]=1;
3.为数组全部元素赋值时,可以不给出数组元素的个数,但此时必须给出所有初值。例:
int a[] = {1,2,3,4,5};
4.数组元素的值全为0时,可以简写为:
int a[100] = {0};
6.1.4 一维数组的应用
(1)获取最大/小值与其位置
【例6.1】编写程序,求 10 个数中的最大值、最小值以及它们的位置。
分析:
求若干个数的最大值可以先将这些数存放在数组中。设置变量 max,使其初值为第一个数,然后从第二个数开始依次与 max 比较,每次比较都将较大者置于 max 中。这样,所有
的数均比较一遍后 max 中存放的一定是最大值。最小值的求得方法亦然。
如果要输出最大值和最小值的位置,可再设两个变量 imax、imin,在比较的同时分别记录最大值、最小值元素的下标即可
#include<stdio.h>
#define N 10
void main()
{
int a[N],k,max,min,imax,imin;
for(k=0;k<N;k++)
scanf("%d",&a[k]);
max = a[0]; //置初值为第一个数
min = a[0];
imax = 0; //置初值为第一个数的下标
imin = 0;
for(k=1;k<N;k++) //依次比较各数,找出最大值max,最小值min
{
if(max < a[k])
{
max = a[k];
iamx = k;
}
if(min > a[k])
{
min = a[k];
imin = k;
}
}
printf("max=a[%d]=%d,min=a[%d]=%d\n",imax,max,imin,min);
}
补充:以上程序也可以只设置变量max和min分别记录最大值和最小值的下标,即位置,则a[max]
就是最大值,a[min]就是最小值。
(2)冒泡排序
【例6.2】冒泡排序
特点:做相邻两个数的比较,将小的数换到后面。
以冒泡降序排序为例:
若要排序的数有 n 个,则需要 n-1 轮排序;第 j 轮排序中,从第一个数开始,依次进行相邻两个数的比较,即第 1 个与第 2 个数比较,第 2 个与第 3 个数比较,……,第 n-j 个与第 n+1-j 个数比较,共比较 n-j 次,若不符合所要求的顺序,则交换两个数的位置;直到第 n+1-j 个数为止,则第 n+1-j 个位置上的数按要求排好,不再参加以后的比较和交换操作。
编写程序时,将待排序数据存入数组中,用双重循环配合进行排序处理,其中外层循环控制排序的轮次,内层循环控制每轮中的各次比较。最终数组数据被按要求排好序。
#define N 4
#include<stdio.h>
void main()
{
int i,j,m,a[N];
for(i=0;i<N;i++)
scanf("%d",&a[i]);
for(j=1;j<=N-1;j++) //共N-1轮排序
for(i=0;i<N-j;i++) //每轮排序要进行N-j次排序
if(a[i]<a[i+1]) //顺序不符合要求时交换位置
{
m = a[i];
a[i] = a[i+1];
a[i+1] = m;
}
for(i=0;i<N;i++)
printf("%5d",a[i]);
}
思考:
1.如果按从小到大排序,只需将要求里的小于号改为大于号
2.若以由后向前顺序比较排序,以内层为例,可改为for(i=N-1;i>0;i–)
(3)选择法排序
【例6.3】选择法排序
特点:每一轮确定一个数的位置,每轮从需排序数据中找出一个最大或最小数。
过程:
若有 n 个数要从小到大(升序)排序,则需要 n-1 轮排序处理。第1 轮排序,从第 1 个数至第 n 个数中找出最小的数,然后将其与第 1 个数交换位置,排好的第一个数不再参加后面的排序。依次类推,第 j 轮排序,从第 j 个数至第 n 个数中找出最小的数,然后与第 j 个数交换位置,确定第 j 个数顺序。
主要程序代码如下:(对N个存放在a数组里的数据进行排序)
for(i=0;i<N-1;i++) //共N-1轮排序
{
pos = i; //pos记录本次需排序的位置
for(j=i+1;j<N;j++) //确定本轮最小值的下标
if(a[pos]>a[j])
pos = j;
if(pos!=i) //最小值不是a[i]时,a[i]与a[pos]互换
{
m = a[pos];
a[pos] = a[i];
a[i] = m;
}
}
(4)插入数据并保持有序性
【例6.4】向一个有序数据系列中插入数据,并保持数据的有序性。
分析:假设有 5 个数构成的升序排列数据系列 12、17、25、34、56,将 20 插入其中后的数据系列为 12、17、20、25、34、56。设置一维数组 n 存放数据,其长度为 8(大于原始数据个数,为插入数据预留存放位置)
n[0] | n[1] | n[2] | n[3] | n[4] | n[5] | n[6] | n[7] |
---|---|---|---|---|---|---|---|
12 | 17 | 25 | 34 | 56 |
步骤:
1.确定插入位置。将要插入数据与数据系列中各数依次比较,直至插入数据较小(如数
据降序排列,则为插入数据较大)。本例中,应为17与25之间。
2.空出插入位置。将插入位置及其后数据依次向后移动一个位置。注意,应从最后一个数开始移动,否则会破坏原有数据。
n[0] | n[1] | n[2] | n[3] | n[4] | n[5] | n[6] | n[7] |
---|---|---|---|---|---|---|---|
12 | 17 | 25 | 34 | 56 |
3.插入数据。
n[0] | n[1] | n[2] | n[3] | n[4] | n[5] | n[6] | n[7] |
---|---|---|---|---|---|---|---|
12 | 17 | 20 | 25 | 34 | 56 |
程序如下(插入任意3个数):
#include<stdio.h>
void main()
{
int i,j,k,num=0,m=5,n[8]={12,17,25,34,56};
printf("original data:");
for(i=0;i<m;i++) //打印原来的数组
printf("%4d",n[i]);
while(1)
{
printf("\nPlease input a insert data:");//输入要插入的数据
scanf("%d",&k);
for(i=0;i<m;i++) //找到需要插入的位置
if(k<n[i])
break;
for(j=m;j>i;j--) //后移数据,空出插入位置
n[j] = n[j-1];
n[i] = k;
m++; //数组已经增加了一个元素,m也要加1
printf("The inserted data:");
for(i=0;i<m;i++)
printf("%4d",n[i]); //打印处理完的数组
printf("\n");
num++; //统计插入数据个数(循环标记),当num=3时,跳出循环
if(num == 3)
{
printf("\nSorry!At most 3 data can be inserted !\n");
break;
}
}
}
(5)删除数据并保证有序性
【例6.5】设有一有序数据系列,任意输入一个数,如在数据系列中则将其删除。
分析:本例在输入要删除数据后,先查询其是否在数据系列中,如不在,则提示用户“数据没找到!”;否则,将该数据从数据系列中删掉。
本例的查询操作是顺序查询,即将要删除数据与数据系列中数据依次比较,看是否相等。在有序数据系列中查找还可使用二分法查找,效率更高。删除数据实际上是做数据的前移,即从要删除数据后面的一个数开始至最后一个数依次前移一个位置。
程序如下:
#include<stdio.h>
void main()
{
int i,j,k,m=5,n[5]={12,17,25,34,56};
printf("original data:");
for(i=0;i<m;i++) //打印原始数组
printf("%4d",n[i]);
printf("\nPlease input a delete data:"); //输入要被删除的数据
scanf("%d",&k);
for(i=0;i<m;i++) //查询是否有该数据
if(k == n[i]) //如果有,确定位置,将该位置之后的数据依次前移,覆盖需要被删除的数据
{
for(j=i;j<m-1;j++)
n[j] = n[j+1]; //实际并未删除最后一个数据,最后一个数据仍保留在数组中,
break; //只不过输出时输出到倒数第二个
}
if(i >= m) //如果正常结束循环,i=m,即未找到数据
printf("data not found!\n");
else
{
printf("The last data:"); //打印删除完后的数组
for(i=0;i<m-1;i++) //循环m-1次,因为已经删除了一个元素
printf("%4d",n[i]);
printf("\n");
}
}
6.2 二维数组
6.2.1二维数组的定义
定义格式:类型说明符 数组名 [常量表达式1] [常量表达式2];
其中“常量表达式 1”表示第一维下标的长度,即数组的行数;“常量表达式 2”表示第二维下标的长度,即数组的列数。下标从 0 开始。
说明:
1.在C语言中,二维数组在存储器中的存放是按行排列的。即先依次存放 0 行数组元素,然后再接着存放1行数组元素,直至最后一行。
2.数组名代表数组的首地址。
a[0][0] | a[0][1] | a[0][2] | a[1][0] | a[1][1] | a[1][2] |
---|---|---|---|---|---|
100 | 104 | 108 | 112 | 116 | 120 |
&a[1][1] = a + (1*3+1)*4 = 100 + 16 = 116
//从左至右,依次为:1:第一个下标;3:列数;1:第二个下标;4:数据所占字节数
3.在 C 语言中,可将二维数组看作一种特殊的一维数组,每一行是其一个元素,而每一个元素又是一个一维数组。例如:int a[2][3];
可将二维数组 a 看成一个一维数组,它有两个元素,用 a[i]表示第 i 行构成的一维数组的数组名,即 a[0]、a[1]。而 a[0]、a[1]均是包含 3 个元素的一维数组。一维数组 a[0]的元素为 a[0][0]、a[0][1]、a[0][2]。须注意,a[0]、a[1]不能当作下标变量使用,它们是数组名,不是一个单纯的下标变量。
6.2.2 二维数组的引用
表示形式:数组名[ 行下标表达式 ] [ 列下标表达式 ]
二维数组元素的输入:(以a[2][3]为例)
for(i=0;i<2;i++)
for(j=0;j<3;j++)
scanf("%d",&a[i][j]);
6.2.3 二维数组的初始化
初始化形式:
数据类型 数组名[整型常量表达式][整型常量表达式]={初始化数据};
将“{}”中的初值依次赋给各数组元素,各初值之间用逗号隔开。
二维数组可按行分段赋值,也可按行连续赋值。例如a[5][3]
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};
说明:
1.初始化的数据个数不能超过数组元素的个数,否则出错。
2.可以只对部分元素赋初值,未赋初值的元素自动取 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};
分行初始化:
int a[2][3]={{1,2},{4}};
可以写为
int a[][3]={{1,2},{4}};
一般,第一维定义省略时,其大小的确定规则为:初值个数能被第二维整除,所得的商就是第一维的大小;若不能整除,则第一维的大小为商再加 1。
6.2.4 二维数组的应用
【例6.6】输出杨辉三角的前 10 行。
分析:
杨辉三角中数据的规律是:第 1 列与对角线上的元素值均为 1,其它位置上的元素值均为上一行同列元素与前一列元素值之和,见下面的例子。假设杨辉三角中某元素为 a[i][j],则上一行同列元素为 a[i-1][j],上一行前一列元素为 a[i-1][j-1]。
例:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
1 6 15 20 15 6 1
...
程序如下:
#incldue<stdio.h>
void main()
{
int i,j,a[10][10];
for(i=0;i<10;i++) //将三角中所有元素置1
for(j=0;j<=i;j++)
a[i][j] = 1;
for(i=2;i<=10;i++) //求除第一列与对角线以外元素的值
for(j=1;j<=i-1;j++)
a[i][j] = a[i-1][j] + a[i-1][j-1];
for(i=0;i<10;i++) //输出杨辉三角
{
for(j=0;j<=i;j++)
printf("%4d",a[i][j]);
printf("\n");
}
}
6.3 符号数组
字符串的存放可以使用字符数组,字符数组中各数组元素依次存放字符串的各个字符。字符数组的数组名代表该字符串的首地址。利用字符数组为处理字符串中字符和引用整个字符串带来了极大的方便。
6.3.1 字符数组的定义及结束标志
1.字符数组的定义
用来存放字符型量的数组称为字符数组。字符数组的定义与数值数组相同。例如:char c[10];
定义了一个具有 10 个元素的字符数组 c,其每一个元素都是字符型的,一个元素存放一个字符。由于字符型和整型通用,也可以定义为:int c[10];
虽然合法,但由于每个整型量占 4 个字节,会浪费存储空间。
字符数组也可以是二维或多维数组,例如:char c[5][10];
2.字符串结束标志
- 当用字符数组处理字符串时,为了测定字符串的实际长度,C 语言规定了一个字符串结束标志,以字符‘\0’作为标志。也就是说,遇到字符‘\0’时,字符串结束,结束标志前面的字符组成字符串。
- 在程序中往往依靠检测‘\0’字符的出现来判定字符串是否结束,而不是根据数组的长度来决定字符串长度。
- 系统对字符串常量也自动加一个‘\0’作为结束符。例如”I am a girl.”共有 12 个字符,但在内存中占 13 个字节,最后一个字节‘\0’是系统自动加上的。
- ‘\0’代表 ASCII 码值为 0 的字符,是一个“空操作符”,输出时不显示字符。
- 设定数组长度时要注意给‘\0’预留位置,例如:char a[6],最多存放5个字符
6.3.2 字符数组的初始化
1.初始化时把字符逐一赋给数组元素。
例如:char c[11]={‘W’,’e’,’l’,’c’,’o’,’m’,’e’,’’,’y’,’o’,’u’};
数组状态如下:
c[0] | c[1] | c[2] | c[3] | c[4] | c[5] | c[6] | c[7] | c[8] | c[9] | c[10] |
---|---|---|---|---|---|---|---|---|---|---|
W | e | l | c | o | m | e | y | o | u |
如果{}中提供的初值个数大于数组长度,则出错;如果初值个数小于数组长度,则剩余的数组元素被自动赋予‘\0’。例如:char a[10]={‘H’,’e’,’l’,’l’,’o’};
数组状态如下:
c[0] | c[1] | c[2] | c[3] | c[4] | c[5] | c[6] | c[7] | c[8] | c[9] |
---|---|---|---|---|---|---|---|---|---|
H | e | l | l | o | \0 | \0 | \0 | \0 | \0 |
当对全体元素赋初值时可以省去长度说明。例如:char c[]={‘W’,’e’,’l’,’c’,’o’,’m’,’e’,’’,’y’,’o’,’u’};
这时 c 数组的长度自动定为 11(此时数组存储的数据为字符常量)
2.用字符串常量对字符数组的初始化。
例如下面两个语句的功能是等价的:
char c[] = {"Welcome you"};
char c[] = "Welcome you";
注意:
- 字符串常量应使用双引号引起来。
- 数组 c 的长度是 12,而不是 11。因为字符串常量的最后由系统加上了一个‘\0’。(此时数组存储的数据为字符串常量)
- 如果字符串的长度小于数组长度,则剩余的数组元素被自动赋予‘\0’。
- 字符数组最后的‘\0’字符不是必须的,但为了处理判断方便最好加上。如:char c[]={‘W’,’e’,’l’,’c’,’o’,’m’,’e’,’’,’y’,’o’,’u’,’\0’};在字符串的末尾人为地加了一个‘\0’,c 数组的长度自动定为 12。
6.3.3 字符数组的输入输出
1.逐个字符输入输出(输入输出时使用格式符“%c”)(此时数组长度可以等于字符数量,因为逐个输入,看做字符常量的组合而非字符串常量)
【例6.8】
#include<stdio.h>
void main()
{
char a[5];
int j;
for(j=0;j<5;j++) //输入
scanf("%c",&a[j]);
for(j=0;j<5;j++) //输出
printf("%c",a[j]);
}
2.字符串整体或部分输入输出(输入输出时使用格式符“%s”)(此时数组长度要大于等于字符数量+1,因为整体输入,看做字符串常量,末尾加‘\0’)
【例 6.9】
#include<stdio.h>
void main()
{
char b[8],c[] = "BASIC\ndBASE";
scanf("%s",b); //输入字符串存入b数组
printf("%s\n",b); //输出b数组中的字符串
printf("%s\n",c);
printf("%s",&c[2]); //输出c数组中从c[2]开始的字符串,&c[2]为字符串首地址,一直输出到‘\0'
}
运行结果为:
输入:123456↙
输出:
123456
BASIC
dBASE
SIC
dBASE
注意:
1.用格式符“%s”输入输出字符串时
- 如果是字符数组,其输入输出项必须是字符数组名,名前不能加地址符“&”,即以字符串的地址形式出现,数组名代表数组的起始地址。例如:printf("%s",b);
- 如果输入输出项是字符数组元素的地址,则表示输入输出内容是从该地址开始的字符串。例如:printf("%s",&c[2]);
- 如果输出项以字符串常量形式出现,表示该字符串的首地址。例如:printf("%s",“abcd”);
2.用格式符“%s”输出的字符串是从输出项提供的地址开始直至碰到字符串结束标志’\0’为止。例如:
char a[10] = "abcd\0123";
printf("a=%s\n",a);
输出:
a=abcd
3.用格式符“%s”不能输入带空格、回车或跳格的字符串。因为空格、回车或跳格是输入数据的结束标志。例如:
char a[10];
scanf("%s",a);
printf("%s\n",a);
运行时,输入:How are you↙
输出:How
修改方法:
- 多设几个字符数组分段存放
- 使用gets()函数
【例6.10】求给定字符串的长度,并将其复制到另一字符串中。
- 分析:用两个字符数组分别存放源字符串和目标字符串。利用循环逐一读取源字符串中的字符,直至碰到字符串结束符,同时进行字符的计数及存入目标字符串的操作。
#incldue<stdio.h>
void main()
{
char s1[80],s2[80];
int i,num = 0;
printf("input string s2:\n");
gets(s2); //获取字符串s2
for(i=0;s2[i]!='\0';i++)//逐个对字符计数与复制
{
num++;
s1[i] = s2[i];
}
s1[i] = '\0'; //在目标字符串末尾加上字符串结束符
printf("num=%d\n",num);
puts(s1); //输出字符串s1
}
6.3.4 字符串处理函数
C语言提供了丰富的字符串处理函数,大致可分为字符串的输入、输出、合并、修改、比较、转换、复制、搜索等几类。
- 使用字符串函数时应包含头文件**“string.h”**
常用字符串处理函数如下:
函数名称 | 调用格式 | 作用 | 说明 |
---|---|---|---|
puts() | puts(字符数组) | 将一个字符串输出到终端设备 | 该函数输出的字符串中可以包含转义字符 |
gets() | gets(字符数组) | 从终端输入一个字符串到字符数组中,并且得到一个函数值 | puts()与 gets()函数只能一次输入一个字符串,即圆括号中只能有一个数组名 |
strcpy() | strcpy(字符数组1,字符数组2) | 将字符数组 2 所指字符串内容复制到字符数组 1 所指存储空间中。函数返回字符数组 1 的首地址,即目的串的首地址 | 字符数组 1 必须有足够的空间存放字符数组 2 中的内容 |
strcat() | strcat(字符数组1,字符数组2) | 将字符数组 2 所指字符串内容连接到字符数组 1 所指字符串后,并自动覆盖字符数组 1 串末尾的‘\0’。函数返回字符数组 1 的地址值 | 字符数组 1 必须有足够的空间存放两个字符串连接后的内容 |
strlen() | strlen(字符数组) | 计算并返回字符数组的长度值 | 返回的长度值不包括结束标志‘\0’ |
strcmp() | strcmp(字符数组1,字符数组2) | 比较字符数组 1 和字符数组 2 所指字符串的大小。若字符数组 1>字符数组 2,函数值大于 0(正数);若字符数组 1=字符数组 2,函数值为0;若字符数组1<字符数组2,函数值小于 0(负数) | 两字符串比较时,按 ASCII码值从左至右逐一比较 |
strlwr() | strlwr(字符串) | 将字符串中的大写字母变为小写 | |
strupr() | strupr(字符串) | 将字符串中的小写字母变为大写 |
1.字符串输出函数 puts() 与字符串输入函数 gets()
- 字符串输出函数puts()中可以使用转义字符,其完全可以由 printf() 函数代替。 当需要按
一定格式输出时,通常使用printf()函数。 - 字符串输入函数gets()不以空格作为字符串输入结束的标志,而只以回车作为输入结束。不同于scanf()函数的是允许输入的字符串中含有空格。
2.字符串连接函数strcat()与字符串赋值函数strcpy()
- 两个函数中的“字符数组 1”应定义足够的长度,以容纳处理后的结果字符串;字符串连接函数 strcat()在连接时,会将前一个字符串后的’\0’去掉,只保留结果字符串最后的’\0’。
- 字符串赋值函数strcpy()中的“字符数组 2”也可以是一个字符串常量,此时相当于把一个字符串赋予一个字符数组,完成一个字符串的整体赋值。用函数strcpy()拷贝时,串结束标志“\0”也一同被赋值。
3.字符串比较函数strcmp()
- 该函数可以比较两个字符数组,也可比较两个字符串常量,或进行字符数组和字符串常量的比较。
- 比较两个字符串时,是自左至右逐个字符相比,直到出现不同的字符或碰到结束符为止。若全部字符相同,则相等;若出现不同字符,则以第一个不同字符的比较结果为准。
注意,两个字符串的比较不能使用以下形式:
if(st1>st2)//str1和str2是两个字符串的地址,自然不能比较
printf("string1>string2");
正确形式为:
if(strcmp(st1,st2) > 0)
printf("string1>string2");
【例6-11】输入五个国家的名称,使其按字母顺序排列输出。
分析:一个国家的名称是一个字符串,需要一个一维数组存放,五个国家的名称应存放在一个二维字符数组中。C语言规定可以把一个二维数组当做若干个一维数组处理,因此本例定义一个二维数组 c[5][18],将其按照五个一维数组 c[0]、c[1]、c[2]、c[3]、c[4]处理,每个一维数组存放一个国家的名字。利用字符串比较函数比较各一维数组的大小,并排序输出结果。
#include<stdio.h>
#include<string.h>
void main()
{
char st[18],c[5][18];
int i,j,p;
printf("input country's name:\n");
for(i=0;i<5;i++)
gets(c[i]); //输入5个国家名字符串
printf("\n");
for(i=0;i<5;i++)
{
p = i;
strcpy(st,c[i]); //把c[i]的值复制给st
for(j=i+1;j<5;j++) //将c[i]之后的位置循环
if(strcmp(c[j],st) < 0) //找出最小的字符串并拷贝到st中
{
p = j;
strcpy(st,c[j]);
}
if(p != i) //存在比c[i]更小的字符串,则交换c[i]与st内容
{
strcpy(st,c[i]);
strcpy(c[i],c[p]);
strcpy(c[p],st);
}
puts(c[i]);
}
printf("\n");
}
【例6-12】判断 s1 字符串中是否包含 s2 字符串。
分析:本例的判断过程实际上是做比较处理,具体过程为:从 s1 字符串的第一个字符开始,依次与 s2 字符串的各字符比较,若均相同,则 s1 包含 s2。否则再从 s1 的下一个字符(第2 个字符)开始,依次与 s2 字符串的各字符比较。设 k1、k2 分别表示 s1 串和 s2 串的长度,则最后一次应从 s1 的第 k1-k2+1 个字符开始(即 s1[k1-k2]),依次与 s2 字符串的各字符比较。若存在不同字符,则 s1 中肯定不包含 s2。
- 举个例子:s1有3个字符,s2有2个字符,最后一次从s1的第k1-k2+1个字符开始,即s1的第2个(3-2+1),即s1[1] (s1[3-2])
#include<stdio.h>
#include<string.h>
void main()
{
char s1[80],s2[80];
int i = 0,j,k,k1,k2,f;
gets(s1);
gets(s2);
k1 = strlen(s1);
k2 = strlen(s2);
f=0; //f为标志位:f = 1,s2包含在s1中;f = 0,s1包含在s2中
while(i < k1 - k2 + 1 && !f) //从s1串的s1[i]字符开始检测s2是否包含在s1
{
j = 0;
k = i;
while(s2[j] && s1[k] == s2[j])//存在不同字符或s2包含在s1中时退出循环
{
j++;
k++;
}
if(s2[j]=='\0') //若退出循环时,s2[j]=='\0',说明s2串包含在s1串中
{
f = 1;
break; //确认s2串包含在s1串中,退出循环
}
i++;
}
if(f == 1)
printf("%s is in %s\n",s2,s1);
else
printf("%s is not in %s\n",s2,s1);
}