C语言教程,数组
学习目的:数组的概念比较简单,但是
由于它的引用,使程序的编制内容更
加贴近实际。通过讲解,使学生熟悉
数组的编程方式、运行方法和程序算法分析。
数组内容介绍
1 一维数组
2 二维数组
3 字符数组
4 字符串处理函数
1、数组的概念:在程序设计中,为了处理方便,把具
有相同类型的若干变量按有序的形式组织起来。这些按
序排列的同类数据元素的集合称为数组。
2、数组属于构造数据类型:一个数组可以分解为多个
数组元素,这些数组元素可以是基本数据类型或是构
造类型。
3、数组的类型:按数组元素的类型不同,数组又可分
为数值数组、字符数组、指针数组、结构数组等各种
类别。
4、声明:本章介绍数值数组和字符数组,其余的在以
后各章陆续介绍。
一 维数组的定义方式为,
类型说明符 数组名 [常量表达式 ];
例如,
int a[10]; 说明整型数组 a,有 10个元素。
float b[10],c[20]; 说明实型数组 b,有 10个元素,实型
数组 c,有 20个元素。
char ch[20]; 说明字符数组 ch,有 20个元素。
强调,
方括号中的常量表达式表示数据元素的个数,也称为
数组的长度,但数组元素的下标从 0开始,到长度 -1为
止。如 int a[5];表示数组 a有 5个元素。但是其下标从 0
开始计算。因此 5个元素分别为 a[0],a[1],a[2],a[3],a[4]。
数组名不能与其它变量名相同。
例如,
main()
{ int a;
float a[10];
…… }
是错误的。
不能在方括号中用变量来表示元素的个数,但是
可以是符号常数或常量表达式。
例如,
#define FD 5
main( )
{ int a[3+2],b[7+FD];
…… } // 是合法的。
但是下述说明方式是错误的。
main( )
{ int n=5;
int a[n];
…… }
一维数组元素的引用
数组元素的一般形式为,数组名 [下标 ]
例如,a[5]; a[i+j]; a[i++]
说明,
1.下标表示了元素在数组中的顺序号。
2.其中下标只能为整型常量或整型表达式。如为小数时,
C编译将自动取整。
3.数组元素也称为下标变量,在C语言中只能逐个地使用
下标变量,而不能一次引用整个数组。
例如,输出有 10个元素的数组必须使用循环语句逐个输出
各下标变量,
for(i=0; i<10; i++)
printf("%d",a[i]);而不能用一个语句输出整个数组。
下面的写法是错误的,printf("%d",a);
举例:分析下面的程序及其执行结果
main( ) //例 1
{ int i,a[10];
for(i=0;i<=9;i++)
a[i]=i;
for(i=9;i>=0;i--)
printf("%d ",a[i]); }
main( ) //例 2
{int i,a[10];
for(i=0;i<10;)
a[i++]=i;
for(i=9;i>=0;i--)
printf("%d",a[i]); }
main( ) //例 3
{ int i,a[10];
for(i=0;i<10;)
a[i++]=2*i+1;
for(i=0;i<=9;i++)
printf("%d ",a[i]);
printf("\n%d
%d\n",a[5.2],a[5.8]); }
上例分析如下,
本例中用一个循环语句给 a数组各元素送
入奇数值,然后用第二个循环语句输出
各个奇数。在第一个 for语句中,表达
式 3省略了。在下标变量中使用了表达
式 i++,用以修改循环变量。当然第二
个 for语句也可以这样作,C语言允许
用表达式表示下标。程序中最后一个printf语句输出了两次 a[5]的值,可以看
出当下标不为整数时将自动取整,小数
部分忽略。
一维数组的初始化
初始化的概念,数组初始化赋值是指在数组定义时给
数组元素赋予初值。数组初始化是在编译阶段进行的。
这样将减少运行时间,提高效率。
初始化赋值的一般形式为,
类型说明符 数组名 [常量表达式 ]={值,值 …… 值 };
例如,int a[10]={ 0,1,2,3,4,5,6,7,8,9 };
相当于 a[0]=0;a[1]=1...a[9]=9;
其中在 { }中的各数据值即为各元素的初值,各值之
间用逗号间隔。
C语言对数组的初始化赋值还有以下几点规定,
1)可以只给部分元素赋初值。
当 { }中值的个数少于元素个数时,只给前面部分元素
赋值。例如,
int a[10]={0,1,2,3,4};表示只给 a[0]~ a[4]5个元素赋值,而
后 5个元素自动赋 0值。
2)只能给元素逐个赋值,不能给数组整体赋值。
例如给十个元素全部赋 1值,只能写为,
int a[10]={1,1,1,1,1,1,1,1,1,1};而不能写为,int a[10]=1;
3)如给全部元素赋值,则在数组说明中,可以不给出数组
元素的个数。
例如,int a[5]={1,2,3,4,5};可写为,int a[]={1,2,3,4,5};
一维数组程序举例可以在程序执行过程中,对数组作
动态赋值。这时可用循环语句配合 scanf函数逐个对数
组元素赋值。 【 例 】 分析如下程序的功能
main( )
{ int i,max,a[10];
printf("input 10 numbers:\n");
for(i=0;i<10;i++)
scanf("%d",&a[i]); /*10个数据之间用空格分隔 */
max=a[0];
for(i=1;i<10;i++)
if(a[i]>max) max=a[i];
printf("maxmum=%d\n",max); }
本例程序中第一个 for语句逐个输入 10个
数到数组 a中。 然后把 a[0]送入 max中。
在第二个 for语句中,从 a[1]到 a[9]逐个与
max中的内容比较,若比 max的值大,则
把该下标变量送入 max中,因此 max总是
在已比较过的下标变量中为最大者。比
较结束,输出 max的值。
【 例 】 选择升序排序
main( )
{ int i,j,p,q,s,a[10];
printf("\n input 10 numbers:\n");
for(i=0;i<10;i++) scanf("%d",&a[i]);
for(i=0;i<10;i++)
{ p=i;q=a[i];
for(j=i+1;j<10;j++)
if(q<a[j]) { p=j;q=a[j]; }
if(i!=p) {s=a[i]; a[i]=a[p]; a[p]=s; }
printf("%d",a[i]); } }
本例程序中用了两个并列的 for循环语句,在第二个 for 语
句中又嵌套了一个循环语句。第一个 for语句用于输入 10
个元素的初值。第二个 for语句用于排序。
本程序的排序采用逐个比较的方法进行。在 i次
循环时,把第一个元素的下标 i赋于 p,而把该
下标变量值 a[i]赋于 q。然后进入小循环,从
a[i+1]起到最后一个元素止逐个与 a[i]作比较,
有比 a[i]大者则将其下标送 p,元素值送 q。一次
循环结束后,p即为最大元素的下标,q则为该
元素值。若此时 i≠p,说明 p,q值均已不是进入
小循环之前所赋之值,则交换 a[i]和 a[p]之值。
此时 a[i]为已排序完毕的元素。输出该值之后转
入下一次循环。对 i+1以后各个元素排序。
一维数组的在内存中的存储结构
设有如下定义,
int x=3,y=4,z=5,a[5];
则系统内存的分配情
况如图,
二维数组定义的一般形式是,
类型说明符 数组名 [常量表达式 1][常量表达式 2]
其中常量表达式 1表示第一维下标的长度,常
量表达式 2 表示第二维下标的长度。
例如,int a[3][4];
说明了一个三行四列的数组,数组名为 a,其
下标变量的类型为整型。该数组的下标变量
共有 3× 4个,即,
a[0][0],a[0][1],a[0][2],a[0][3]
a[1][0],a[1][1],a[1][2],a[1][3]
a[2][0],a[2][1],a[2][2],a[2][3]
二维数组在概念上是二维的,即是说其下标在两
个方向上变化。
二维数组下标变量在数组中的位置处于一个平
面之中,而不是象一维数组只是一个向量。但
是,实际的硬件存储器却是连续编址的,也就
是说存储器单元是按一维线性排列的。如何在
一维存储器中存放二维数组,可有两种方式:
一种是按行排列,即放完一行之后顺次放入第
二行。另一种是按列排列,即放完一列之后再
顺次放入第二列。在C语言中,二维数组是按
行排列的。
数组 a在内存中的存储结构如下,
二维数组元素的引用
表示的形式为:数组名 [下标 ][下标 ]
注意数组元素的引用与数组的定义形
式:两者具有完全不同的含义。数组
说明的方括号中给出的是某一维的长
度,即可取下标的最大值;而数组元
素中的下标是该元素在数组中的位置
标识。前者只能是常量,后者可以是
常量,变量或表达式。
【 例 】 一个学习小组有 5个人,每个人有三门课的考试成
绩。求全组分科的平均成绩和各科总平均成绩。
张 王 李 赵 周
Math 80 61 59 85 76
C 75 65 63 87 77
Foxpro 92 71 70 90 85
可设一个二维数组 a[5][3]存放五个人三门课的成绩。再设
一个一维数组 v[3]存放所求得各分科平均成绩,设变量
average 为全组各科总平均成绩。编程如下,
main( )
{ int i,j,s=0,average,v[3],a[5][3];
printf("input score\n");
for(i=0;i<3;i++)
{ for(j=0;j<5;j++)
{ scanf("%d",&a[j][i]); s=s+a[j][i];}
v[i]=s/5; s=0; }
average =(v[0]+v[1]+v[2])/3;
printf("math:%d\nc
languag:%d\ndbase:%d\n",v[0],v[1],v[2]);
printf("total:%d\n",average ); }
二维数组初始化也是在类型说明时给各下标变量赋以初
值。二维数组可按行分段赋值,也可按行连续赋值。
例如对数组 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};
这两种赋初值的结果是完全相同的。
【 例 】 分析如下程序的运行结果
main( )
{ int i,j,s=0,average,v[3],
a[5][3]={{80,75,92},{61,65,71},{59,63,70},{85,87,90},{7
6,77,85}};
for(i=0;i<3;i++)
{ for(j=0;j<5;j++) s=s+a[j][i];
v[i]=s/5; s=0; }
average=(v[0]+v[1]+v[2])/3;
printf("math:%d\nc
languag:%d\ndFoxpro:%d\n",v[0],v[1],v[2]);
printf("total:%d\n",average); }
对于二维数组初始化赋值还有以下说明,
1)可以只对部分元素赋初值,未赋初值的元素自动取 0值。
例如,int a[3][3]={{1},{2},{3}}; 是对每一行的第一列
元素赋值,未赋值的元素取 0值。 赋值后各元素的值
为,
1 0 0
2 0 0
3 0 0
int a [3][3]={{0,1},{0,0,2},{3}}; 赋值后的元素值为,
0 1 0
0 0 2
3 0 0
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};
3)数组是一种构造类型的数据。二维数组可以看作是由一
维数组的嵌套而构成的。设一维数组的每个元素都又
是一个数组,就组成了二维数组。当然,前提是各元
素类型必须相同。根据这样的分析,一个二维数组也
可以分解为多个一维数组。C语言允许这种分解。
如二维数组 a[3][4],可分解为三个一维数组,其数组名分
别为,a[0],a[1],a[2]
对这三个一维数组不需另作说明即可使用。这三个一维
数组都有 4个元素,例如:一维数组 a[0]的元素为
a[0][0],a[0][1],a[0][2],a[0][3]。
必须强调的是,a[0],a[1],a[2]不能当作下标变量使用,它
们是数组名,不是一个单纯的下标变量。
字符数组,用来存放字符量的数组称为字符数组。
字符数组的初始化,字符数组也允许在定义时作初始化
赋值。
例如,char c[10]={‘c’,‘ ’,‘p’,‘r’,‘o’,‘g’,‘r’,‘a’,’m’};
赋值后各元素的值为,
数组 C
c[0]的值为‘ c’ c[1]的值为‘ ’ c[2]的值为‘ p’
c[3]的值为‘ r’ c[4]的值为‘ 0’ c[5]的值为‘ g’
c[6]的值为‘ r’ c[7]的值为‘ a’ c[8]的值为‘ m’
例如,char c[]={`c`,` `,`p`,`r`,`o`,`g`,`r`,`a`,`m`}; 这时 C数
组的长度自动定为 9。
字符数组的引用
【 例 】 阅读分析
main( )
{ int i,j;
char a[][5]={{'B','A','S','I','C',},{'d','B','A','S','E'}};
for(i=0;i<=1;i++)
{ for(j=0;j<=4;j++)
printf("%c",a[i][j]);
printf("\n");
}}
字符串和字符串结束标志
C语言中用字符串用字符数组来存放。
字符串与字符数组的区别是,字符串中有‘ \0’,
一般的字符数组没有‘ \0’,不能作为字符串来
处理。
千变万变有一点不变:在对字符串处理时,以
‘ \0’作为识别一个字符串的标志。
注意这样的几个“长度”,字符数组的长度、
字符串的长度、字符串所占字节的长度。
char c1[]=”C program”;
char c2[10]={‘C’,’\0’,’J’,’a’,’v’,’a’,’\0’,’V’,’B’,’A’};
C\0Java\0VBA/*字符串 c1的长度,所占字节数,
数组 c1的长度; */
/*字符串 c2的长度,是否可以把 c2+2作为一个字
符串处理,把 c2+7作为一个字符串处理会带来
什么样的问题?但是若改为,
char c2[11]={‘C’,’\0’,’J’,’a’,’v’,’a’,’\0’,’V’,’B’,’A’};
C\0Java\0VBA?问题会消失吗? */
分析 char c[10]={‘1’,’2’};中每一个元素的值。
字符数组所独有的赋初值方法:用字符串对数组作初
始化赋值。
分析,char c1[]={‘c’,‘ ’,‘p’,‘r’,‘o’,‘g’,‘r’,‘a’,‘m’};与
char c2[]={"C program"}; 与
char c3[]="C program";
的区别? c2和 c3的数组长度相同,c1的长度要少一个。
用字符串方式赋值比用字符逐个赋值要多占一个字节,
用于存放字符串结束标志‘ \0’。上面的数组 c在内存
中的实际存放情况为,
C program\0 ‘\0'是由 C编译系统自动加上的。由于采
用了‘ \0'标志,所以在用字符串赋初值时一般无须指
定数组的长度,而由系统自行处理。
c p r o g r a m \ 0
字符数组的输入输出
【 例 】 分析程序
main()
{
char c[]="BASIC\ndBA0SE";
printf("%s\n",c);
}
注意在本例的 printf函数中,使用的格式字符串
为,%s”,表示输出的是一个字符串。而在输
出表列中给出数组名则可。不能写为,
printf("%s",c[]);
main()
{
char st[15];
printf("input string:\n");
scanf("%s",st);
printf("%s\n",st);
}
注意,
输入的字符串长度必须小于 15,以留出一个字节用于存放字符串结束标志 `\0`。
对一个字符数组,如果不作初始化赋值,则必须说明数组长度。 char st[];错。
当用 scanf函数输入字符串时,字符串中不能含有空格,否则将以空格作为串的结
束符。
例如当输入的字符串中含有空格时,运行情况为,
input string,
this is a book
输出为,
this
从输出结果可以看出空格以后的字符都未能输出。为了避免这种情况,可多设几
个字符数组分段存放含空格的串。
程序可改写如下,
【 例 】
main()
{
char st1[6],st2[6],st3[6],st4[6];
printf("input string:\n");
scanf("%s%s%s%s",st1,st2,st3,st4);
printf("%s %s %s %s\n",st1,st2,st3,st4);
}
本程序分别设了四个数组,输入的一行字符的空格分段分别装
入四个数组。然后分别输出这四个数组中的字符串。
scanf函数中,字符数组前不允许有 &。这是为什么呢?
这是由于在C语言中规定,数组名就代表了该数组的首地址。整 个数组是以首地址开头的一块连续的内存单元。
更加深入的理解字符串的处理:从指定的地址处开始,直到碰 到’ \0’为止
字符串处理函数
特别说明:用于输入输出的字符串函数,在使用前应包含头文件 "stdio.h",使用其
它字符串函数则应包含头文件 "string.h"。
下面介绍几个最常用的字符串函数。
1,字符串输出函数 puts
格式,puts (字符数组名 )
功能:把字符数组中的字符串输出到显示器。 即在屏幕上显示该字符串。
【 例 】
#include"stdio.h"
main()
{
char c[]="BASIC\ndBASE";
puts(c);
}
puts函数完全可以由 printf函数取代。当需要按一定格式输出时,通常使用 printf函
数。
2,字符串输入函数 gets
格式,gets (字符数组名 )
功能:从标准输入设备键盘上输入一个字符串。
本函数得到一个函数值,即为该字符数组的首地址。
【 例 】
#include"stdio.h"
main()
{
char st[15];
printf("input string:\n");
gets(st);
puts(st);
}
gets函数并不以空格作为字符串输入结束的标志,而只以回车作
为输入结束。这是与 scanf函数不同的。
3,字符串连接函数 strcat
格式,strcat (字符数组名 1,字符数组名 2)
功能:把字符数组 2中的字符串连接到字符数组 1 中字符串的后面,并
删去字符串 1后的串标志,\0”。本函数返回值是字符数组 1的首地址。
【 例 】
#include"string.h"
main()
{
static char st1[30]="My name is ";
int st2[10];
printf("input your name:\n");
gets(st2);
strcat(st1,st2);
puts(st1);
}
要注意的是,字符数组 1应定义足够的长度,否则不能全部装入被连接的
字符串。
4,字符串拷贝函数 strcpy
格式,strcpy (字符数组名 1,字符数组名 2)
功能:把字符数组 2中的字符串拷贝到字符数组 1中。串结束标
志,\0”也一同拷贝。字符数名 2,也可以是一个字符串常量。这
时相当于把一个字符串赋予一个字符数组。
【 例 】
#include"string.h"
main()
{
char st1[15],st2[]="C Language";
strcpy(st1,st2);
puts(st1);printf("\n");
}
字符数组 1应有足够的长度,否则不能全部装入所拷贝的字符串。
5,字符串比较函数 strcmp
格式,strcmp(字符数组名 1,字符数组名 2)
功能:按照 ASCII码顺序比较两个数组中的字符串,并由函数返回值返回比较结果。
字符串 1=字符串 2,返回值= 0;
字符串 1〉 字符串 2,返回值 〉 0;
字符串 1〈 字符串 2,返回值 〈 0。
本函数也可用于比较两个字符串常量,或比较数组和字符串常量。
【 例 】
#include"string.h"
main()
{ int k;
static char st1[15],st2[]="C Language";
printf("input a string:\n");
gets(st1);
k=strcmp(st1,st2);
if(k==0) printf("st1=st2\n");
if(k>0) printf("st1>st2\n");
if(k<0) printf("st1<st2\n");
}
本程序中把输入的字符串和数组 st2中的串比较,比较结果返回到 k中,根据 k值再输出结果提
示串。当输入为 dbase时,由 ASCII 码可知,dBASE”大于,C Language”故 k〉 0,输出结果
,st1>st2”。
6,测字符串长度函数 strlen
格式,strlen(字符数组名 )
功能:测字符串的实际长度 (不含字符串结束标志
‘ \0’) 并作为函数返回值。
【 例 】
#include"string.h"
main()
{ int k;
static char st[]="C language";
k=strlen(st);
printf("The lenth of the string is %d\n",k);
}
结束