? 4.1 数组
4.1.1 一维数组
4.1.2 二维数组
4.1.4 字符数组
4.2 结构体
4.2.1 结构体
4.2.2 结构体数组
4.5 典型例题第四章 构造数据类型
4.1.6 数组与函数
4.1.7 数组与指针
4.1.8 字符串与指针
4.2.3 结构体程序举例
4.2.4 结构体与指针
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]后下一个存储单元 赋值为 32,但这样可能会破坏数组以外其他变量的值
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++)
scanf(“%d”,&a[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 0 3 0 5 0 7 0 9 0 11 0 13 0 15 0 17 0 …
0 0 0 0 0 5 0 7 0 0 0 11 0 13 0 0 0 17 0 …
第 1步,先将 1筛掉第 2步,将 2的倍数都筛掉第 3步,将 3的倍数都筛掉逐步将 4,5… 10的倍数都筛掉,最后数组中的非零数就是素数
#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用以存储当前最小数的下标
选择排序的改进方法一,二维数组的定义
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.1.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.1.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.1.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.1.4 字符数组一,字符数组和字符串
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.1.4 字符数组错误的初始化三,字符串的输入
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.1.4 字符数组
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.1.4 字符数组四,字符串的输出
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.1.4 字符数组例,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.1.4 字符数组存在隐患错五、字符串处理函数 (用字符串函数时要写 #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.1.4 字符数组例,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.1.4 字符数组存在隐患
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.1.4 字符数组
3,字符串比较函数
格式,strcmp ( 字符串 1,字符串 2 )
作用,比较两个字符串的大小
说明,
① 两个字符串可能是字符串常量或字符数组变量
② 两个字符串比较时,从字符串中的第一个字符开始逐个比较其 ASCII码值,直到出现不同字符或出现‘ \0’为止
③ 比较的结果由函数值带回
str1 = = str2 函数值为 0
str1 > str2 函数值为正数
str1 < str2 函数值为负数
4.1.4 字符数组例,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.1.4 字符数组
4,测字符串长度函数
格式,strlen (字符串 )
作用,测出字符串中实际字符的个数 ( 不包括‘ \0’ )
例,int len1,len2 ;
char s[10] ;
len1 = strlen(,computer”) ;
gets(s) ;
len2 = strlen(s) ;
5,字符串中大、小字母转换
strlwr (字符串 ) 将字符串中大写字母换成小写字母
strupr (字符串 ) 将字符串中小写字母换成大写字母
4.1.4 字符数组
4.1.4 字符数组例 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.1.4 字符数组
num用来统计单词个数,word
是判别是否为单词的标志,若
word=0表示未出现单词,如出现单词 word就置成 1
c不是空格,若 c前面的字符是空格,表明这是一个新单词的开始,
则 word=1,num加 1; 若 c前面的字符不是空格,则不作任何处理
4.1.4 字符数组例 4-12 编程实现两个字符串的连接,但不能使用 strcat函数
a b c d \0
x y z \0
x y z \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.1.4 字符数组
i 和 j分别是 s1 和 s2的下标
当元素 i不是‘ \0’ 时,
让 i加 1,指向下个元素
依次将 s2的元素 j赋给 s1的元素 i
s1[i]=s2[j];
i++; j++;?S1末尾赋
‘ \0’
4.1.6 数组与函数一、数组元素作函数参数其用法与变量作实在参数一样,是单向传递二、数组名作函数参数 (实参和形参都使用数组名 )
说明,
1,必须在主调函数和被调函数中分别定义数组
2,实参数组和形参数组的类型必须一致
3,数组名作参数就是将实参数组的 首地址 传给形参数组即实参数组和形参数组占用同一片内存单元
4,定义形参数组时可以不指定其大小,
当形参数组发生变化时,实参数组也随之发生变化
4.1.6 数组与函数例 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.1.6 数组与函数数组占用存储空间
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.1.6 数组与函数例 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.1.7 数组与指针一,一维数组与指针
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.1.7 数组与指针
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.1.7 数组与指针
(2) 引用数组元素下标法 地址法 指针法第 k个元素 a[k] *(a+k) *(p+k)
第 k个元素的地址 &a[k] a+k p+k
注意,指针变量也可以加下标,即,p[k] 等价于 a[k]
① 分别用三种方法输出数组元素,其效率不同,
下标法与地址法的效率相同,指针法的效率较快
② 用指针变量访问数组元素时要注意下标是否越界
4.1.7 数组与指针
#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.1.7 数组与指针例 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.1.7 数组与指针
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.1.7 数组与指针例 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.1.7 数组与指针例 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.1.7 数组与指针
4.1.7 数组与指针例 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.1.8 字符串与指针一、字符指针
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.1.8 字符串与指针二,字符数组与字符指针变量的区别
1,存储方式不同,
字符数组在定义时,不论是否进行初始化,都会为其分配存储空间,用来存放字符串中的字符和‘ \0’
字符指针是分配一个指针变量的存储单元,用于存放地址。如果字符指针定义时没有进行初始化,则不会为其分配字符串的存储空间;如果定义时进行了初始化,则还要分配一块连续内存空间存储字符串,并将存储空间的起始地址赋给字符指针。
char s1[5],s2[8] =“Program”;
P r o g r a m \0
s1
s2
char *p1,*p2=“abc”;
4.1.8 字符串与指针
p1
p2
a
b
c
\0
2460
2461
2462
2463
2460
4.1.8 字符串与指针二,字符数组与字符指针变量的区别
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.1.8 字符串与指针例 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.1.8 字符串与指针三、字符串指针作函数参数例 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.1.8 字符串与指针例 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.1.8 字符串与指针
4.2 结构体
4.2.1 结构体的引出及使用
在实际问题中我们常需要把不同类型的几个数据组合起来,构成一个整体,如学校中教师和学生的信息。
学号 姓名 性别 年龄 入学成绩 所属学院
0501 李明 男 19 610 信息
0502 张莉 女 19 595 信息
0503 王涛 男 20 580 控制职工编号 姓名 性别 民族 出生日期 职称 学历 单位 工龄
1997025 孙杰 男 汉 1974.9 讲师 大学 信息 9
2001016 赵玫 女 回 1978.3 助教 大学 控制 5
1985104 郑毅 男 汉 1964.11 教授 大学 管理 21
4.2.1 结构体的引出及使用学号 姓名 性别 年龄 入学成绩 所属学院
0501 李明 男 19 610 信息
0502 张莉 女 19 595 信息
0503 王涛 男 20 580 控制如何表示这样的数据信息?
结构体是由一些逻辑相关,但数据类型不同的分量组成的一组数据。
学号 姓名 性别 年龄 入学成绩 所属学院
int num char name[10] char sex int age int score char institute[20]
一、结构体的引出
4.2.1 结构体的引出及使用例 4-20 输入三个学生的信息,并输出
#include <stdio.h>
void main( )
{ struct student
{ int num ;
char name[10] ;
char sex;
int age;
int score ;
char institute[20]
};
struct student s1,s2,s3;
定义学生的结构体类型定义 3个结构体类型的变量,
用来存放 3个学生的信息
4.2.1 结构体的引出及使用
void main( )
{,
scanf(“%c %d %d %d”,&s1.sex,&s1.num,&s1.age,&s1.score);
gets(s1.name);
gets(s1.institute);
scanf(“%c %d %d %d”,&s2.sex,&s2.num,&s2.age,&s2.score);
gets(s2.name); gets(s2.institute);
scanf(“%c %d %d %d”,&s3.sex,&s3.num,&s3.age,&s3.score);
gets(s3.name); gets(s3.institute);
输入一个学生的信息
printf(“%6d %10s %2c %3d,,s1.num,s1.name,s1.sex,s1.age);
printf(“%5d %20s \n”,s1.score,s1.institue);
printf(“%6d %10s %2c %3d,,s2.num,s2.name,s2.sex,s2.age);
printf(“%5d %20s \n”,s2.score,s2.institue);
printf(“%6d %10s %2c %3d,,s3.num,s3.name,s3.sex,s3.age);
printf(“%5d %20s \n”,s3.score,s3.institue);
}
输出学生信息
struct student
{ int num ;
char name[10] ;
char sex;
int age;
int score ;
char institute[20]
};
二、结构体的定义及使用
4.2.1 结构体的引出及使用注意,定义了结构体类型,仅仅是定义了数据的组织形式,创立了一种数据类型,但并不会为这种结构体类型分配内存空间只有定义了结构体变量,才会为变量分配空间注意不要忘了分号成员表列
1、结构体类型的定义,
struct 结构体类型名
{ 数据类型 成员名 1;
数据类型 成员名 2;
:,
数据类型 成员名 n;
} ;
关键字用户定义的标识符
4.2.1 结构体的引出及使用
2,定义结构体变量的方法
(1) 先定义结构体类型,再定义变量
struct student
{ char name[10] ;
int age ;
int s1,s2 ;
} ;
struct student st1,st2 ;
st1
st2
name
age
s1
s2
name
age
s1
s2
内存中结构体变量占有一片连续的存储单元,其占用的字节数可用 sizeof 运算符 算出,
printf(“%d\n”,sizeof(struct student) ) ;
printf(“%d\n”,sizeof(st1) ) ;
结构体变量 st1和 st2各自都需要 16个字节的存储空间结构体类型定义结构体变量定义
4.2.1 结构体的引出及使用
2,定义结构体变量的方法
(2) 定义结构体类型同时定义变量
struct student
{ char name[10] ;
int age ;
int s1,s2 ;
} st1,st2 ;
(3) 直接定义结构体变量
struct
{ char name[10] ;
int age ;
int s1,s2 ;
} st1,st2 ;
注意,这里没有结构体类型名这种方式有时使用并不方便因此不建议大家采用定义结构体变量
4.2.1 结构体的引出及使用例,struct date
{ int year ;
int month ;
int day ;
} ;
struct stud
{ char name[10] ;
struct date birthday ;
int s1,s2 ;
} ;
结构体类型可以嵌套定义或,struct stud
{ char name[10] ;
struct date
{ int year ;
int month ;
int day ;
} birthday ;
int s1,s2 ;
} ;
4.2.1 结构体的引出及使用
3、结构体变量的引用格式,结构体变量名,成员名
struct student
{ char name[10] ;
int age ;
int s1,s2 ;
} ;
struct student st1 ;
st1,name =,Mary” ;
st1,age = 21 ;
st1,s1 = 78 ;
st1,s2 = 86 ;
说明:
(1) 一般情况下都是对结构体变量的成员进行赋值和输入 \输出
(2) 特殊情况下结构体变量可以进行整体赋值
(3) 可以引用结构体变量的地址和其成员的地址
struct date
{ int year ;
int month ;
int day ;
} ;
struct stud
{ char name[10] ;
int age ;
struct date birthday;
int s1,s2 ;
} ;
struct stud st2 ;
int age,year ;
st2,name =,John” ;
st2,age = 20 ;
st2,birthday,year = 1980 ;
st2,birthday,month = 11 ;
st2,birthday,day = 23 ;
st2,s1 = 89 ;
st2,s2 = 95 ;
age = 24 ;
year = 2000 ;
可以定义与结构体成员名相同名字的变量,它们之间不会发生混乱
相同类型的结构体变量可以进行 整体赋值
struct date
{ int year ;
int month ;
int day ;
} ;
struct stud
{ char name[10] ;
int age ;
struct date birthday;
int s1,s2 ;
} ;
struct stud st1,st2,st3;
st1,name =,John” ;
st1,age = 20 ;
st1,birthday.year = 1980 ;
st1,birthday.month = 11 ;
st1,birthday.day = 23 ;
st1,s1 = 89 ;
st1,s2 = 95 ;
st2=st1;
st3,name=“Mary”;
st3,age=20;
st3,birthday=st1,birthday;
st3,s1 = 76;
st3,s2 = 85;
注意要正确赋值的条件是变量 st1已经有了数据
4.2.1 结构体的引出及使用
4.2.1 结构体的引出及使用
4、结构体变量的初始化
struct student
{ char name[10] ;
int age ;
int s1,s2 ;
} st1={“Mary”,21,78,86} ;
struct stud
{ char name[10] ;
struct date birthday ;
int s1,s2 ;
} ;
struct stud st2={,John”,1980,11,23,89,95 } ;
struct student
{ char name[10] ;
int age ;
int s1,s2 ;
} ;
struct student st1;
st1={“Mary”,21,78,86} ;初始化,正确这是赋值,错误
C不允许这么做初始化,正确
5,结构体变量的输入 输出
C语言不允许结构体变量整体进行输入和输出,
只能对结构体变量的成员进行输入和输出
gets( st1,name ) ;
scanf(,%d%d%d”,&st1,birthday,year,
&st1,birthday,month,&st1,birthday,day ) ;
scanf (,%d%d%d”,&st1,age,&st1,s1,&st1,s2 ) ;
puts( s1,name ) ;
printf(,%4d”,st1,age );
printf(,%d,%d,%d”,st1,birthday,year,
st1,birthday,month,st1,birthday,day ) ;
printf(“%4d %4d\n”,st1,s1,st1,s2 ) ;
4.2.1 结构体的引出及使用
4.2.2 结构体数组的引出及引用学号 姓名 性别 年龄 入学成绩 所属学院
0501 李明 男 19 610 信息
0502 张莉 女 19 595 信息
0503 王涛 男 20 580 控制一、结构体数组的引出一个结构体变量只能存放一个学生的信息,对于多个学生的信息,可以使用一个结构体数组来存放,
结构体数组的每个元素是一个结构体类型的变量定义结构体数组的方法与定义普通数组的方法类似,
结构体类型 数组名 [数组的长度 ];
4.2.2 结构体数组的引出及引用例 4-21 输入 30个学生的信息,并输出
#include <stdio.h>
void main( )
{ struct student
{ int num ;
char name[10] ;
char sex;
int age;
int score ;
char institute[20]
};
struct student s[30];
int i;
for (i=0;i<30;i++)
{ scanf(“%c%d %d %d”,&s[i].sex,
&s[i].num,&s[i].age,&s[i].score);
gets(s[i].name);
gets(s[i].institute);
}
for (i=0;i<30;i++)
{ printf(“%6d %10s %2c %3d,,s[i].num,
s[i].name,s[i].sex,s[i].age);
printf(“%5d %20s\n”,
s[i].score,s[i].institue);
}
}
4.2.2 结构体数组的引出及引用二、结构体数组的定义及使用
1、定义结构体数组
(1) 先定义结构体类型再定义结构体数组
struct student
{ char name[10] ;
int age ;
int s1,s2 ;
} ;
struct student st[6] ;
(2) 定义结构体类型的同时定义结构体数组
struct student
{ char name[10] ;
int age ;
int s1,s2 ;
} st[6] ;
(3) 直接定义结构体数组
struct
{ char name[10] ;
int age ;
int s1,s2 ;
} st[6] ;
不提倡使用该方法
4.2.2 结构体数组的引出及引用
2、结构体数组的初始化将每个数组元素的数据用花括号 { } 括起来
struct student
{ char name[10] ;
int age ;
int s1,s2 ;
} ;
struct student st[3]={ {“Mary”,21,78,86},
{“Alex”,20,90,80},{“Mike”,19,75,68} };
Mary
21
78
86
Alex
20
90
80
Mike
19
75
68
st[0]
st[1]
st[2]
4.2.2 结构体数组的引出及引用
(2) 数组元素之间可以整体赋值也可以将一个元素赋给一个相同类型的结构体变量
struct student st[3]={ {“Mary”,21,78,86},{“Alex”,…} } ;
struct student x ;
st[2] = st[0] ;
x = st[1] ; 都是结构体变量的整体赋值形式
3,结构体数组的引用
(1) 引用某个数组元素的成员例,puts( st[0],name ) ;
printf(“%d,%d”,st[1],age,st[1],s1 ) ;
(3)输入和输出操作只能对数组元素的 成员 进行分析,
假设有 3个候选人,共有 100个人投票定义一个结构体数组,它有 3个元素代表
3个候选人,每个元素有 2个成员,一个是候选人名字,一个是得票数 (初始时为 0)
例 4-22 设计一个对候选人得票进行统计的程序候选人 票数
Mike 0
John 0
Alex 0
有 100张选票,输入选票上的名字,然后判断是谁的名字,就将谁的票数加 1,重复 100次最后输出结构体数组
4.2.3 结构体程序举例例 4-22 程序代码#include<stdio.h>
#include <string.h>
struct person
{ char name[10];
int count;
};
void main( )
{ struct person cand[3]={{“Li”,0},{“Zhang”,0},{“Fun”,0}};
int i,j; char cname[20];
for(i=0; i<100; i++)
{ scanf(“%s”,cname);
for(j=0; j<3; j++)
if( strcmp(cname,cand[j].name)= =0) cand[j].count++;
}
for(i=0; i<3; i++)
printf(“%10s,%d\n”,cand[i].name,cand[i].count);
}
定义候选人的结构体类型对结构体数组进行初始化
//输入选票上的名字
//若有 100人投票,则循环 100次
//将选票上的名字依次和候选人的名字比较
//选票上名字和某个候选人名字相同时,其票数加 1
4.2.3 结构体程序举例例 4-23 按成绩对学生信息进行从高到底的排序
#include <stdio.h>
#define N 30
struct stud
{ int n ;
char name[10] ;
int s ;
};
void input(struct stud a[ ])
{ int i ;
for ( i=0 ; i<N ; i++)
scanf(“%d %s %d”,&a[i].n,a[i].name,&a[i].s) ;
}
void output(struct stud a[ ])
{ int i ;
for ( i=0 ; i<N ; i++)
printf(“%4d %10s %4d”,a[i].n,a[i].name,a[i].s) ;
}
注意 a[i].name前不加 &,因
name是数组名,因用 %s,
输入时名字不能加空格
4.2.3 结构体程序举例
void sort(struct stud a[ ] )
{ int i,j ;
struct stud temp;
for ( i=0 ; i<N-1 ; i++)
for ( j=i+1 ; j<N ; j++)
if ( a[i].s<a[j].s )
{ temp=a[i] ; a[i]=a[j] ; a[j]=temp ; }
}
void main( )
{ struct stud st[N];
input(st);
sort(st);
output(st);
}
注意进行比较的是元素 a[i]
和 a[j]的成绩成员 s,但进行交换的是元素 a[i]和 a[j]
4.2.4 结构体与指针一,指向结构体变量的指针
1,定义
struct student
{ char name[20] ;
int age ;
int s1,s2 ;
} ;
struct student stu,*p ;
p = &stu ;
2,成员的引用格式
(1) 结构体变量名,成员名
stu,name
gets( stu,name );
(*p),age = 21 ;
p -> s1 = 87 ;
p -> s2 = 90 ;
(2) (*指针变量名 ),成员名
(*p),age
(3) 指针变量名 -> 成员名
p -> s1
4.2.4 结构体与指针二,指向结构体数组的指针
1,定义 struct student a[3],*p ;
2,使用 for ( p=a ; p<a+3 ; p++ )
{ gets( p->name ) ;
scanf(,%d%d%d”,&p->age,&p->s1,&p->s2); }
三,结构体变量作为函数参数
1,函数实参和形参都用结构体变量,参数之间为 值传递实参结构体变量 各成员的值依次传给 形参结构体变量
2,返回结构体类型值的函数定义格式,结构体类型名 函数名 ( 形参表 )
{ 函数体 ; }
例,struct student funct ( int x,float y )
{ 函数体 ; }
注意 结构体类型是已经定义好的
4.2.4 结构体与指针
#include <stdio.h>
#define N 5
struct stud
{ char name[10] ;
int s[3] ;
int sum,ave ;
} ;
struct stud count (struct stud x)
{ int i ;
x.sum = 0 ;
for ( i = 0; i<3 ; i++ )
x.sum = x.sum + x.s[i] ;
x.ave = x.sum / 3 ;
return(x);
}
例 4-24,求学生成绩的总分和平均分 (结构体变量作参数 )
//定义学生的结构体类型
//计算学生三门课的总分
//返回学生的全部信息
//结构体变量作参数返回结构体类型的值
4.2.4 结构体与指针
void main ( )
{ struct stud a[N] ;
int j ;
for ( j=0; j<N; j++ )
scanf(“%s%d%d%d,,a[j].name,&a[j].s[0],
&a[j].s[1],&a[j].s[2] );
for ( j=0; j<N; j++)
a[j]=count ( a[j] ) ;
for ( j=0; j<N; j++)
printf(“%10s%4d%4d%4d%6d%4d\n”,a[j].name,
a[j].s[0],a[j].s[1],a[j].s[2],a[j].sum,a[j].ave );
}
//函数调用,将 a[j]的值传给 count函数的参数 x,并将返回值赋给 a[j]
//输入 N个学生的信息
//定义结构体数组 a,存放 N个学生的信息
4.5 典型例题例 4-25 折半查找
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-25 程序代码
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 程序代码
4.7 用指针处理链表一,、基本概念
1,动态存储分配,根据需要临时分配内存单元用以存放数据,
当数据不用时可以随时释放内存单元
2,链表,是可以动态地进行存储分配的一种数据结构它是 由一组动态数据链接而成的序列
3,结点,链表中的每一个动态数据称为一个结点
4,结点类型,是一个包含指针项的结构体类型一般由两部分组成,
(1) 数据成员,存放数据
(2) 指针成员,存放下一个结点的地址
struct sd
{ int num;
int score;
struct sd *next ;
} ;
数据成员指针成员头指针 表头结点 表尾结点
2010
head 2010 1428 1570
95
1428
1
86
1570
2
82
NULL
3
NULL为空地址,
表示链表到此结束二、简单链表
#include <stdio.h>
struct sd
{ int num;
int score;
struct sd *next ;
} ;
void main( )
{ struct sd a,b,c,*head,*p;
head=&a;
a.num=1; a.score=95; a.next=&b;
b.num=2; b.score=86; b.next=&c;
c.num=3; c.score=82; c.next=NULL;
p=head;
while(p!=NULL)
{ printf(“%3d%4d\n”,p->num,p->score);
p=p->next;
}
}
head 2010 1428 1570
2010
a b c
1
95
1428
2
86
1570
3
82
NULL
p 201014281570NULL
说明,该程序虽然建立了一个链表,但这个链表是静态的,因为它的结点个数是固定的,不能按需要增加新的结点,也不能按需要删除结点,这样的链表并不实用,我们需要的是“动态链表”
三,处理动态链表所需的函数 ( 需用头文件 <stdlib.h> )
1,malloc 函数原型,void *malloc( unsigned int size )
作用,在内存中开辟一个长度为 size 的连续存储空间,
并将此存储 空间的起始地址带回注意,
(1) 函数返回值是指针,但该指针是指向 void类型的,因此在使用时希望这个指针指向其他类型需要用强制类型转换
(2) 如果内存缺少足够大的空间进行分配,则 malloc 函数返回值为“空指针” (即 NULL)
例,struct sd *p;
p = (struct sd * ) malloc ( sizeof(struct sd) ) ;
强制类型转换结构体类型占用的字节长度
2,free函数原型,void free( void *ptr )
作用,将指针变量 ptr指向的存储空间释放注意,
(1) ptr的值不是任意的地址,必须是程序中执行 malloc
函数所返回的地址
(2) 模型中 ptr是 void型,但调用 free函数时,参数可能是其他类型,计算机系统会自动进行转换例,struct sd *p;
p=(struct sd *) malloc (sizeof(struct sd)) ;
free(p); p 42004200
四,链表应用举例
1,建立链表 (表尾添加法 )
#include <stdio.h>
#include <stdlib.h>
#define ST struct student
ST
{ int num ;
int score ;
ST *next ;
};
#define LEN sizeof(ST)
int n ;
宏定义 ST,以后书写简单求出结构体类型占用的字节数全局量,n 表示结点的个数创建一个由 head指向的链表
ST * creat(void)
{ ST *head,*p1,*p2 ;
n=0 ; head=NULL ; p1=(ST *) malloc ( LEN ) ;
scanf(,%d %d”,&p1->num,&p1->score ) ;
while ( p1->num!=0 )
{ n=n+1;
if (n= =1) head=p1 ;
else p2->next=p1 ;
p2 = p1 ;
p1 = (ST *) malloc (LEN) ;
scanf(,%d %d”,&p1->num,&p1->score ) ;
}
p2->next = NULL ; return( head ) ;
}
head 2010 1428 1570
NULL
p1 p2
3264
2010
2010
1
95
1428
n 0123
20101428
2
86
3
82
0
0
1428 1570 NULL
1570 15703264
2,输出链表
(将 head指向的链表从头到尾输出一遍)
void list ( ST *head )
{ ST *p ;
p = head ;
while ( p!=NULL )
{ printf(“%3d %4d\n”,p->num,p->score ) ;
p = p->next ;
}
}
head 2010 1428 1570
2010 1
95
1428
2
86
1570
3
82
NULL
p 201014281570NULL
p1 20101428
num 2
p2 2010
head 2010 1428 1570
2010 1
95
1428
2
86
1570
3
82
NULL1570
n 32
3,链表的删除
(在 head指向的链表中,删除数据域为 num的结点)
ST *del ( ST *head,int num )
{ ST *p1,*p2 ;
p1 = head ;
while ( ( num!=p1->num) && (p1->next!=NULL) )
{ p2 = p1 ; p1 = p1->next ; }
if ( num = = p1->num )
{ if ( p1= =head ) head=p1->next ;
else p2->next=p1->next ;
free( p1 ) ; n=n-1 ;
printf(,deleted ! \n”) ;
}
else printf(,can not delete!\n”) ;
return( head ) ;
}
4,链表的插入
ST *insert ( ST *head )
{ ST *p0,*p1,*p2 ;
p1=head ;
p0=( ST *) malloc ( LEN ) ;
scanf (“%d%d”,&p0->num,&p0->score) ;
if ( head= =NULL)
{ head = p0 ; p0->next = NULL ; }
else { while ( ( p0->num > p1->num ) && ( p1->next!=NULL) )
{ p2 = p1 ; p1 = p1->next ; }
if ( p0->num<p1->num )
{ if ( head= =p1) head = p0 ; else p2->next =p0 ;
p0->next = p1 ;
}
else { p1->next = p0 ; p0->next = NULL; }
}
n++ ; return( head ) ;
}
2010 1428 1570head
2010 1
95
1428
3
86
1570
5
82
NULL
n 34
p1 p2
p0
2680
2680
2010 2010 4
75
14281570 1428
2680
1570
2010 1428 1570head
2010 2
95
1428
3
86
1570
5
82
NULL
n 34
p1 p2p0
2680
2680 2010
1
75
插入的结点作为表头
2010
2680
2010 1428 1570head
2010 2
95
1428
3
86
1570
5
82
NULL
n 34
p1 p2 p0
2680
26802010
8
75
插入的结点作为表尾
NULL
20101428 14281570
2680
5,main 函数
void main ( )
{ ST *h ;
int delnum ;
h = creat( ) ;
list ( h ) ;
scanf(“%d”,&delnum ) ;
h = del ( h,delnum ) ;
list ( h ) ;
h = insert ( h ) ;
list ( h ) ;
}
/* 建立一个链表 */
/* 输出链表 */
/* 输入要删除的学号 */
/* 删除学号为 delnum的结点 */
/* 在链表中插入一个结点 */