第六章数组.docx
《第六章数组.docx》由会员分享,可在线阅读,更多相关《第六章数组.docx(25页珍藏版)》请在冰豆网上搜索。
第六章数组
第六章数组
前面讲的简单类型变量只含一个成员。
数组是一种构造类型(称为数组元素),数组类型的变量也简称数组,它是由固定数目的同类型成员(称为数组元素)组成的构造类型变量。
构造类型的最低一级成员必须是简单类型,它可以如同一个简单类型的变量一样,被赋值或出现在表达式中。
C的构造类型有:
数组、结构和联合。
6.1数组的说明
6.1.1数组的说明
1.说明区分符数组名[[常量表达式]][=初值表],…;
2.说明:
(1)说明区分符可以是存储类型区分符(auto,extern,static)、类型区分符(int,char,float等)和类型限制符(const,volatile)之一或它们的组合,存储类型区分符说明数组变量的存储类型、类型区分符说明数组元素的类型。
(2)数组名是一个标识符。
(3)外层[]是一维数组的标志,内层[]表示可选。
(4)常量表达式的值必须是正整数,它是数组的长度(或大小)说明,即说明数组含有的元素数目。
(5)数组元素在数组中有一定的次序关系,C中数组元素从0开始编号。
(6)定义性说明时必须给出长度说明,引用性说明或参数类型说明时不需要长度,但[]不能省。
(7)“=初值表”是可选的,它显式地说明数组的初值,引用性说明时不能说明初值。
下面是一些数组说明的例子。
Intx[5];
定义x是含有5个int元素的数组(或称x是含有5个元素的int数组),其元素依次表示为x[0],x[1],x[2],x[3],x[4]。
如果说明在函数外,则x是外部数组;如果说明在函数体内,则x为自动数组,由于没有给出初值说明,因此,如果x是外部数组,则所有元素的初值均为0,如果x是自动数组,则其元素的值不确定。
Floata[30];
定义a是含有30个元素的float数组,其元素依次为:
a[0],a[1],…,a[29],共30个。
Doubleva[maxval];
Val是含有maxval个元素的double数组(maxval是已定义的符号常量)。
数且说明可以和其它同类型对象在一个说明语句中说明。
例如:
floatx,a[50],b[100];
charc,str[100],buf[300];
(8)静态数组说明必须给出static类型区分符,如
staticcharline[200];如果说明在函数外,则为外部静态数组,如果在块内,则为局部静态数组。
数组长度说明可以含有运算符的表达式形式,表达式的值必须为正整数,且不能含有变量或未定义的标识符。
如:
#defineSIZE100
chartext[SIZE*2+1];
而下面定义是错误的:
intn;
charname[n];
因为C语言不允许定义动态数组,编译时数组的大小必须是已知的。
且其大小在程序执行过程中是固定不变的。
6.1.2数组引用
数组的引用有两种形式:
一是引用数组的元素,例如:
intx[5],则x[0],x[1],…,[4]是对数组x的5个元素的引用。
二是引用数组名。
一般数组的输入、输出、赋值和基本运算都必须对数组的元素逐个进行。
数组名可以用作函数调用的参数(实参),输入输出字符串,给指针变量赋值等。
数组各元素之间是按顺序排列的。
它们对应的存储单元一个紧接一个,每个元素在数组中的位置即元素编号称为元素的下标,每个元素用数组名和各自的下标两部分表示。
称为数组元素的引用。
引用形式如下:
数组名[下标表达式]
下标表达式(简称下标)可以为任意整型表达式,包括整型常量、变量、含有运算符的整型表达式,甚至是值为整数的函数调用。
下标表达式的值应在元素编号的取值范围内(在C语言中,下标不作超界检查),对于长度为n的数组,下标表达式的取值为0,1,2,…,n-1,C中数组的编号规定从0开始,对数组最低一级元素的引用称为下标变量,其性质等同于类型简单变量。
如intx[10],i;
x[5]=3+2;
i=x[0]+x[3];
例1计算并输出全班30个学生C语言程序设计课程的平均成绩以及每个人的成绩与平均成绩之差。
#include“stdio.h”
#definenumbers30
intmain(void)
{inti;
floatx[numbers],sum,average;
sum=0;
printf(“input%dscores:
\n”,numbers);
for(i=0;i{scanf(“%f”,&x[i]);
sum+=x[i];
}
average=sum/numbers;
printf(“average=%.2f\n”,average);
for(i=0;iprintf(“x[%d]-average=%.2f\n”,i,x[i]-average);
return0;
}
输出:
intpu30scores:
输入:
8879615058…
average=71.80
x[0]-average=16.20
x[1]-average=..
…
数组可以作为函数的参数来传递,形参说明为数组,对应的实参是数组的名字。
C语言中数组的名字不是一个变量,而是数开头元素的地址,即a[0]的地址(称为数组的首地址),它是在编译时确定的一个地址常量,例如数组名a和&a[0]代表同一个地址值。
因此参数传递时传递给被调用函数形参的值是实参的首地址,而不是对实参数组元素的复制,通过形参引用的数组元素不是实参数组元素的副本,而是实参数组元素本身。
所以被调用函数中对数组元素的任何修改,都是直接修改实参数组元素本身。
例2输入n个整数,将它们按从小到大的次序排列,然后输出。
分析:
排序的方法很多,本例采用冒泡法排序,从第0个元素开始。
数组a[0]a[1]a[2]a[3]a[4]
数组的初始值823165947
第一轮比较结束316594782
第二轮比较结束319476582
第三轮比较结束931476582
第四轮比较结束931476582
两两比较,将较大的元素移到后一个位置,经一轮比较后最大元素被移到最后一个元素的位置,称为冒泡,下一轮从第0个元素开始对余下的n-1个元素重复上述过程。
Voidsort(intv[],intn);
{inti,j,temp;
for(i=1;ifor(j=0;jif(v[j]>v[j+1])
{temp=v[j];
v[j]=v[j+1];
v[j+1]=temp;
}
}
#include“stdio.h”
#defineN5
intmain(void)
{inta[N],i;
printf(“intpu%dnumbers:
\n”,N);
for(i=0;iscanf(“%d”,&a[i]);
sort(a,N);
for(i=0;iprintf(“%8d”,a[i]);
return0;
}
6.1.3数组的初始化
数组定义时可以通过在说明符中指出初值表来初始化。
(1)初值表用{}括起来,每个初值用逗号隔开。
在ANSIC中,外部数级、静态数组和自动数组均可初始化。
如:
floatx[5]={1,2,1,2,1};/*x[0],x[1],x[2],x[3],x[4]分别初始化为1,2,1,2,1。
(2)如果数组长度与初值个数相同,则在数组说明符中可以不指出数组长度。
或者说不指出数组长度,则其长度由初值个数决定。
上例等价于:
floatx[]={1,2,1,2,1};
(3)初值个数可以小于数组元素的个数(部分初始化),剩余的值为0,如:
floatx[5]={1,2,1};
初值1,2,1依次赋给x开头的三个元素。
X[0],x[1],x[2]。
未指出初值的其余元素x[3],x[4]被初始化为相应类型的缺少值(对于外部数组和静态数组,本例为x[3]为0.0;x[4]为0.0)或初值不确定(对于自动数组),例如:
staticfloatx[5]={2,2,2}
元素x[0],x[1],x[2]值为2,x[3],x[4]值为0.0。
注意标准C规定,当初值个数小于数组元素个数时,初值必须是最前面的连续元素的初值。
上例可以写成staticfloatx[5]={2,2,2};
但不能写成staticfloatx[5]={,2,2,2};
(4)如果初值个数大于定义时指定的数组长度,则为语法错误,例如:
intscore[4]={60,60,43,45,56,57,67}为非法。
6.1.4数组的运算
我们讨论的数组的运算指数组的输入、输出、赋值和以数组元素为操作数的基本运算等操作。
数组的赋值、输入和输出一般要逐个元素进行,对数组元素所允许的操作与同类类型的变量相同。
对数组的所有元素进行操作一般用循环结构来完成。
例如:
intx[3];
用赋值运算给x的元素赋值:
x[0]=10;x[1]=20;x[2]=30;
等价于:
for(i=0;i<3;i++)
x[i]=(i+1)*10;
或:
for(i=0;i<3;i++)
scanf(“%d”,&x[i]);
输出时就为:
for(i=0;i<3;i++)
printf(“x[%d]=%4d”,i,x[i]);
例3:
二分查找函数
分析:
在元素按递增排列的一个有序数组中,查找一个为给定值x的元素的位置(用下标表示)。
如果x在数组中返回x在数组中的下标,否则返回-1。
Intbinsearch(intx,intr[],intn)
{inttop=0,bottom=n-1;mid;
while(top<=bottom)
{mid=(top+bottom)/2;
if(xbottome=mid-1;
elseif(x>v[mid])
top=mid+1;
elsereturn(mid);
}
return–1;
}
例4:
Shell排序函数
分析:
将n个数放在一维数且中,开始时,被分成两段,把数组后一段的每个元素和前一段相隔n/2的元素进行比较,如果后者小于前者,则交换两个元素的值;然后将比较间隔逐次减小一半,当比较间隔减小到0时,整个算法结束,排序完成。
Voidshellsort(intr[],intn)
{intgap,i,j,temp;
for(gap=n/2;gap>0;gap/=2)
for(i=gap;ifor(j=i-gap;j>=0&&v[j]>v[j+gap];j-=gap)
{temp=v[j];
v[j]=v[j+gap];
v[j+gap]=temp;
}
}
6.2字符数组
6.2.1字符数组的说明和引用
类型为char的一维数组称为字符数组,说明形式同一般数组,如:
charbuf[100];
定义buf是含有100个元素的一个字符数组(buf的每个元素是一个字符)。
C语言没有字符串类型,字符数组可以作为字符串变量看待。
对于一个长度为n的字符数组,它可以存储长度不超过n-1的任何一个字符串,多出的一个元素是为了在字符串的末尾存入一个空字符’\0’(值为0)作为字符串的结束标志,存放在字符数组中的实际字符个数称为字符串的实际长度。
’\0’不是字符吕的组成部分,所以定义数组时,所指出的数组长度必须比实际字符串可能的最大长度大1。
对于程序中出现的任何字符串常,编译程序自动在其存储的末尾添一个’\0’作为结束标志。
用户在自己的程序中对字符串须作同样的处理,这样做的好处是(1)处理字符串进可以不指出串的实际长度,因为字符串实际长度是变化的,通常不知道一个实际的字符串含有多少个字符,通过扫描字符串发现’\0’以确定其长度;(2)为了与编译程序保持一致。
例如:
用printf的%s格式输出一个字符串(字符数组作为参数)时必须以’\0’结束。
字符数组的引用、赋值和其它运算与普通数组相同,必须逐个元素进行。
不同的是它可以通过字符数组名由函数scanf和printf用%s输入和输出整个字符串,而不必用循环语句对其元素逐个输入或输出。
如:
scanf(“%s”,buf);/*长度不超过99个字符*/
printf(“%s\n”,buf);
注意:
由于数组名本身已是数组的首地址,所以scanf参数中的buf前面不能再加取地址运算符&,scanf用%s格式输入一字符串时自动在末尾加’\0’;printf用%s格式输出一个字符串时要求字符串必须具有’\0’结束标志。
6.2.2字符数组的初始化
字符数组显式初始化有两种等价的形式:
1.一种形式与一般的一维数组显式初始化的形式相同,即逐个元素指出其初值,例如:
charbuf[100]={‘h’,’e’,’l’,’l’,’o’,’\n’,’\0’};
buf的初值是字符串”hello\n”。
注意用这种形式指定初值时必须在最后一个初值之后明确写上’\0’。
2.初始化的另一种形式是用字符串常量用为初值,如:
charbuf[100]=”hello\n”;
(1)这种初始化方式,编译程序自动在末尾加上’\0’;
(2)若不作长度说明:
如
charbuf[]=”hello\n”;则buf的长度为7。
注意:
若将说明写成charbuf=”hello\n”;则是错误的,此时认为buf是一个字符型变量。
如果buf说明为charbuf[100],则下列用法也是错误的:
buf=”hello\n”;/*数组名不是左值*/
buf[]=”hello\n”;
buf[100]=”hello\n”;
6.2.3字符串处理函数
C语言中字符数组是应用最普遍的数组类型,它用于表示长度可变的字符串,因而字符数组可以称为字符串变量,本节讨论字符数组的应用,以字符串为参数的函数(字符串处理)的编写方法。
例5.输入若干行文本,输出其中最长的那一行。
#include“stdio.h”
#defineMAXLINE100
intgetline(char[],int);
voidcopy(char[],char[]);
intmain()
{intlen;/*当前长长度*/
intmax;/*最大行长度*/
charline[MAXLINE];
charlongest[MAXLINE];
max=0;
printf(“inputlineendofctrl+z\n”);
while((len=getline(line,MAXLINE))>0)
0
if(len>max)
{max=len;
非0
0
copy(line,longest);
}
if(max>0)
非0
printf(“longestlineis\”%s\”,length=%d\n”,\
longest,max);
return0;
}
intgetline(chars[],intline)
{intc,i;
i=0;
While(--line>0&&(c=getchar())!
=EOF&&c!
=’\n’)
s[i++]=c;
if(c==’\n’)s[i++]=’\0’;
return(i);
}
voidcopy(charfrom[],charto[])
{inti=0;
while((to[i]=from[i])!
=’\0’)
i++;
}
输出:
inputlineendofctrl+e:
输入:
structintnode{
intdata;
structintnode*next;
};
输出:
longestlineis“structintnode*next’”,length=22
例6.定义一个函数从一个字符串中删去指定值的所有字符。
Voidsqueeze(chars[],intc)
{inti=0;j=0;
for(;s[i]!
=’\0’,i++)
if(s[i]!
=c)s[j++]=s[i]);
s[i]=’\0’;
}
例7.定义将两个字符串连接成一个字符串的函数,即编写函数strcat。
Voidstrcat(chars[],chart[])
{inti=0,j=0;
while(s[i]!
=’\0’)
i++;
While((s[i]=t[j])!
=’\0’)
{i++;
j++;
}
}
例8.将一个整数转换成对应的数字串。
#include“stdio.h”
#include“string.h”
#defineINTLEN6
voiditoa(intn,chars[])
{inti,sign;
voidreverse(char[]);
if((sign=n)<0)
n=-n;
i=0;
do{
S[i++]=n%10+’0’;
}while((n/=10)>0);
if(sign<0)
s[i++]=’-‘;
s[i]=’\0’;
reverse(s);
}
intmain(void)
{intn;
chars[INTLEN];
printf(“intputainteger:
”);
scanf(“%d”,&n);
itoa(n,s);
printf(“string:
%s\n”,s);
return0;
}
voidreverse(chars[])
{intc,i,j;
for(i=0,j=strlen(s)-1;i{c=s[i];
s[i]=s[j];
s[j]=c;
}
}
例9.定义计算字符串长度的函数。
Intstrlen(chars[])
{inti=0;
while(s[i]!
=’\0’)
i++;
Returni;
}
例10.定义去掉字符串尾部空白字符的函数。
Inttrim(chars[])
{inti;
for(i=strlen(s)-1;i>=0;i--)
if(s[i]!
=’‘&&s[i]!
=’\t’&&s[i]!
=’\n’)
break;
s[i++]=’\0’;
returni;
}
例11.定义将十六进制数字转换成整数的函数,假定字符串中不含任何非法字符。
函数返回值是转换结果。
Inthtoi(chars[])
{inti,n;
n=0;
for(i=0;s[i]!
=’\0’;i++)
{if{s[i]>=’0’&&s[i]<=’9’}
n=n*16+s[i]-‘0’;
if{s[i]>=’a’&&s[i]<=’f’}
n=n*16+s[i]-‘a’+10;
if{s[i]>=’A’&&s[i]<=’F’}
n=n*16+s[i]-‘A’+10;
};
returnn;
}
例12。
定义比较两个字符串的函数,若相等,则返回值1,否则返回值0。
Intstrcmp(chars[],chart[])
{inti=-1;
while(++i,s[i]==t[i]&&s[i]!
=’\0’)
;
return(s[i]==’\0’&&t[i]==’\0’?
1:
0);
}
6.3多维数组
一维数组由具有一个下标的元素组成,它表示一个线性的数据队列,为了表示具有两个下标的元素组成的二维数据队列,如距阵或需要两个以上的下标的元素,则使用多维数组,维即元素的下标具有的下标个数,称为数组的维数。
6.3.1多维数组的说明、引用和存储结构
n维数组说明的一般形式为:
存储类型区分符类型区分符数组名[常量表达式1][常量表达式2]…[常量表达式n][=初值表]
数组名是一个标识符,常量表达式1到常量表达式n分别说明数组第1到第n维的长度,如:
floata[3][2];
定义a是一个二维数组,它表示一个3行2列的矩阵,数组元素(即矩阵元素)都是float类型,每个元素由行下标(第一维)和列下标(第二维)唯一确定,a的元素表示为:
a[i][j](i=0,1,2;j=0,1)。
a的逻辑结构表示为:
二维数组在内存的物理存储结构是按行存储的,
即:
a[0][0],a[0][1],a[1][0],a[1][1],a[2][0],a[2][1]
类似地可以推广到n维数组,以三维数组为例:
intb[3][2][4];
定义b是一个3行2列,每列含有4个int元素的三维数组,b的元素表示为:
第三维
3
b[i][j][k](i=0,1,2;j=0,1;k=0,1,2,3);
2
b的逻辑结构:
元素
b[1][1][3]
1
物理存储结构:
0
b[0][0][0]
第0行,第0列元素,4个
0
b[0][0][1]
1
b[0][0][2]
2
b[0][0][3]
第0行,第1列元素,4个
第二维
1
0
b[0][1][0]
第一维
b[0][1][1]
b[0][1][2]
b[0][1][3]
…
第2行,第1列元素,4个
b[2][1][0]
b[2][1][1]
b[2][1][2]
b[2][1][3]
C语言中,数组元素各维下标是用[]分开表示的。
如b[i][j][k],而不能写成b[i,j,k],编译程序处理多维数组的方法与一维数组相同,即n维数组处理为以n-1维数组为元素的一维数组。
如:
floata[3][2];
两维数组a处理为以a[0],a[1],a[2]为元素的一维数组,每个元素a[i](i=0,1,2)以是具有2个元素的float数组。
a[0]是第一个数组的首地址,同&a[0][0];a[1]等价于&a[1][0];a[2]等价于&a[2][0];a[i][j]是最低一维元素,通常称为数组的元素,等同一个float变量。
6.3.2多维数组的初始化
多维数组的显式初始化与一维数组的不同之处在于初值表示的形式上不同。
以二维数组为例:
定义一个表示下列矩阵的数组,初始化为:
inta[3][3]={{1,4,5},{2,5,8},{3,6,9}};
等价于:
inta[][3]={{1,4,5},{2,5,8},{3,6,9}};
也等价于:
inta[3][3]={1,4,5,2,5,8,3,6,9};/*以