C语言教材第7章Word文档下载推荐.docx
《C语言教材第7章Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《C语言教材第7章Word文档下载推荐.docx(33页珍藏版)》请在冰点文库上搜索。
一组具有相同类型的数据的有序集合,例如一个班学生的学习成绩。
从这个定义中可以看出数组的两个特点:
①数组中的数据具有相同类型;
②数组中的数据是有序的(此处的“有序”指的是数据之间的相对顺序,并非指数值的大小顺序)。
第二节一维数组
一、一维数组的定义
一维数组的定义方式为:
类型说明符数组名[常量表达式];
例如:
intarray[10];
定义了一个整型数组,数组的名称是array,数组的大小是10,即该数组有10个元素。
说明:
(1)“类型说明符”可以是之前所学过的基本类型中的任何一种。
例如定义如下数组:
charname[12];
/*定义一个字符型数组name*/
longdigits[30];
/*定义一个长整型数组digits*/
floatnum[100];
/*定义一个单精度浮点型数组num*/
(2)数组名命名规则和变量名命名规则相同,都遵循标识符命名规则。
(3)常量表达式表示数组中元素个数,即数组的长度。
常量表达式中可以包括常量和符号常量,不能包含变量。
也就是说,在定义数组时必须确定其大小,不允许对数组的大小作动态定义。
请分析以下几个例子中数组定义是否正确。
①inta[10],b[20];
②#defineNUM30
floatf[2*NUM+1];
③intn=10;
intarr[n];
④intn;
scanf("
%d"
&
n);
intaa[n];
第一个例子中定义了两个整型数组a和b,a数组长度是10,b数组长度是20;
第二个例子中定义了一个符号常量NUM,浮点型数组f的长度是61;
这两种定义方式是正确的。
第三个例子中n虽然赋值为10,但n是变量,只不过进行了初始化;
第四个例子是在程序中临时输入数组的大小,这两种定义方式都是用变量定义数组的大小,所以是错误的。
(4)数组所占内存空间的大小取决于数组的类型和数组的长度。
例如有如下定义:
intb[10];
编译程序将为数组b开辟一块连续的存储单元存放数组元素,数组名b表示存储单元的首地址。
在VC6.0环境下,每个int类型的数据占用4个字节的空间,所以该数组总共占用40个字节的空间。
二、一维数组元素的引用
数组和简单变量一样,必须遵循“先定义,后使用”的原则。
定义了一个一维数组以后,就可以像使用简单变量一样使用数组中的每一个元素。
C语言规定:
只能逐个引用数组元素而不能一次引用整个数组。
数组元素的引用采用数组名加下标的方式,形式为:
数组名[下标]
(1)下标可以是整型常量或整型表达式,例如:
array[0]=array[2]+array[4]-array[2*4]
(2)注意定义数组时用到的“数组名[常量表达式]”和引用数组元素时用到的“数组名[下标]”的区别。
inti=10;
inta[i];
/*用变量定义数组,是错误的*/
a[i]=a[i-2]+a[i-1];
/*程序中用变量或表达式作为下标引用数组元素,是正确的*/
(3)引用数组元素时,下标的范围是0~[数组长度-1]。
例如在数组array中,数组的10个元素分别是:
array[0],array[1],array[2],array[3],array[4],array[5],array[6],array[7],array8],array[9]。
请注意:
array[9]是数组中的最后一个元素,即第十个元素,在数组中不存在array[10]这个元素。
出于执行速度的考虑,C并不检查数组下标是否越界。
例如,如果在程序中出现了如下代码:
printf("
%d\n"
array[10]);
array[20]=790;
编译器不会发现这样的错误,程序能通过编译并运行,第一个输出语句输出了其它某个位置上的数据,而第二个赋值语句则会把数据放在可能由其它数据使用的位置上,因而会造成其它某个变量值被修改,造成程序结果的不可预料甚至使程序崩溃。
因此,在使用数组时一定要注意数组下标不能越界。
数组的优势要得到发挥,必须与之前学过的循环结构结合起来使用,下面先看一个简单的例子。
例7.1一维数组元素的引用。
#include<
stdio.h>
voidmain()
{
inti,a[10];
for(i=0;
i<
=9;
i++)/*a[0]~a[9]赋值为0~9*/
a[i]=i;
for(i=9;
i>
=0;
i--)/*将a[9]~a[0]的值输出*/
printf("
%d"
a[i]);
printf("
\n"
);
}
程序运行结果:
9876543210
请想一想,如果不用数组来实现该程序的功能,应该怎么编写程序呢?
与这个程序比较,是否可以看出使用数组的优势呢?
三、一维数组的初始化
数组元素和变量一样,可以在定义时赋初值,称为数组的初始化。
一维数组初始化的格式为:
类型说明符数组名[常量表达式]={常量列表};
常量列表中的各个常量值之间用逗号隔开,初始化方法有以下几种:
(1)对数组中所有元素赋初值,例如:
inta[10]={2,4,6,8,10,12,14,16,18,20};
经过上面的定义和初始化之后:
a[0]=2,a[1]=4,a[2]=6,a[3]=8,a[4]=10,a[5]=12,a[6]=14,a[7]=16,a[8]=18,a[9]=20。
注意,如果常量列表中数值数目大于数组长度,编译器会提示错误信息“toomanyinitializers(初始化太多)”。
(2)对数组中的部分元素赋初值,例如:
inta[10]={2,4,6,8,10,12,14};
a[0]=2,a[1]=4,a[2]=6,a[3]=8,a[4]=10,a[5]=12,a[6]=14,a[7]=0,a[8]=0,a[9]=0。
也就是说,如果只对数组中的部分元素赋初值,那么没有赋初值的元素系统自动将其值设置为0。
(3)如果想使一个数组中全部元素值为0,可以写成:
inta[10]={0,0,0,0,0,0,0,0,0,0};
或inta[10]={0};
请注意,如果想使一个数组中全部元素值为除0以外的其它值,则不能写成后一种形式。
即如果定义:
inta[10]={1,1,1,1,1,1,1,1,1,1};
不能写成:
inta[10]={1};
后一种形式下,系统认为只有a[0]=1,而其它9个元素的值为0。
(4)给数组中所有元素赋初值时可以不用指定数组的长度,编译器会根据常量列表中数值数目来确定数组的大小。
例如inta[10]={2,4,6,8,10,12,14,16,18,20};
可写成:
inta[]={2,4,6,8,10,12,14,16,18,20};
例7.2一维数组的初始化。
voidmain()
{inta[5]={2,4,6,8,10};
intb[5]={3,5,7};
intc[]={1,2,3,4,5};
intd[5]={0};
inte[5]={8};
inti,f[5];
5;
i++)printf("
a[i]);
b[i]);
c[i]);
d[i]);
e[i]);
f[i]);
246810
35700
12345
00000
80000
-858993460-858993460-858993460-858993460-858993460(该行输出可能有所不同)
看最后一行输出结果,是不是莫名其妙?
数组f没有进行初始化,与变量相似,在对数组进行初始化之前,数组中各个元素的值是不确定的,输出结果也就无法确定。
四、一维数组程序举例
在学习了一维数组的相关知识后,我们再回头看看本章开始所提出的排序问题。
排序是计算机程序设计中的一种重要操作,计算机科学家研究了很多种排序方法,其中冒泡排序法是一种效率较高的排序方法。
例7.3对N个数用冒泡排序(BubbleSort)进行由小到大的排序。
假定对N个数,a0,a1,…aN-1进行排序,其基本思想如下:
将a0与a1进行比较,如果a0大于a1,则a0与a1进行交换,然后比较a1与a2,如果a1大于a2,则a1与a2进行交换。
依次类推,直到aN-2与aN-1进行过比较为止,完成第一趟排序,需要进行N-1次两两比较,其结果是aN-1是N个数中的最大者。
然后进行第二趟排序,将前N-1个数(a0与aN-2)进行同样操作,需要进行N-2次两两比较,其结果是aN-2是N个数中的次大者。
同理进行第三、第四……第N-1趟排序,在第i趟排序中需进行N-i次两两比较。
程序代码如下:
#defineN10/*定义字符常量N*/
inti,j,a[N],temp;
Pleaseinput%dnumbers:
N);
N;
i++)
scanf("
a[i]);
N-1;
i++)/*外层循环,进行N-1趟排序*/
for(j=0;
j<
(N-1)-i;
j++)/*内层循环,实现在每一趟排序中进行(N-1)-i次比较*/
if(a[j]>
a[j+1])
{
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
Theresultofsortingis:
Pleaseinput10numbers:
8978675645342312-30-56↙(↙表示回车键,下同)
-56-301223344556677889
在该程序中,定义了一个符号常量N,其作用有两点:
①使得整个程序中数组的大小能够保持一致;
②该程序具有通用性,无论要排序的数的个数是多少,在程序中只需要修改符号常量N的值即可,而不需要修改其它涉及到数组大小的地方。
在进行程序设计时注意符号常量的使用通常会减轻程序员的工作量。
例7.4求Fibonacci数列的前40个数。
Fibonacci数列问题在前面已经分析过:
F1=1n=1
F2=1n=2
Fn=Fn-1+Fn-2n≥3
程序如下:
#include<
#defineN40
voidmain()
inti;
longf[N]={1,1};
/*定义长整型数组f,初始化前两个值都为1*/
for(i=2;
i++)/*从第三个数开始赋值*/
f[i]=f[i-2]+f[i-1];
{
if(i%4==0)printf("
/*控制每行输出四个数*/
%16ld"
1123
581321
345589144
233377610987
1597258441816765
10946177112865746368
75025121393196418317811
514229832040134********309
35245785702887922746514930352
241578173908816963245986102334155
在该程序中,同样定义了一个符号常量N,通过对N值的设定,可以求Fibonacci数列的前任意个数。
但请注意,N值不可超出46,即N≤46,否则超出长整型范围(-2147483648-2147483647),结果将出现负数(如果您对二进制补码有所了解的话就能知道原因)。
有兴趣的读者可上机验证。
第三节二维数组
之前所讨论的数组只有一个下标,称之为一维数组。
但有时用一维数组存储数据并不是很合适。
例如要存储如下一个3行4列矩阵中的元素:
用一维数组存储,可以定义一个长度为12的一维数组,但这样无法区分矩阵的行和列;
另一种方法,定义3个一维数组,每个数组的长度为4,这样能够明显看出矩阵的行列数,但试想一下,如果要存储的不是3行,而是30、300或更多行,这样定义数组合适吗?
显然是不合适的。
于是您可能想到:
能否定义一个数组,在这个数组里可以同时定义行数和列数呢?
答案是肯定的,您所想的就是在C语言里所允许构造的二维数组。
一、二维数组的定义
二维数组的定义方式为:
类型说明符数组名[常量表达式1][常量表达式2];
其中“常量表达式1”表示第一维下标的长度(行数),“常量表达式2”表示第二维下标的长度(列数)。
inta[3][4];
定义了一个3行4列的整型二维数组。
可以这样理解该二维数组:
a是一个包含了3个元素(a[0]、a[1]、a[2])的一维数组,每个元素又是包含了4个int型数据的数组。
如图7-1所示:
图7-1
二维数组的长度=常量表达式1×
常量表达式2。
上面定义的a数组长度是3×
4,即12,所占内存空间是12个int型所占的空间(即12×
4=60字节)。
用二维视图表示数组便于直观地想象,实际上数组在物理存储上是顺序存放的。
如上述定义的a数组,在内存中先存放第一行元素a[0][0]、a[0][1]、a[0][2]和a[0][3],再存放第二行元素a[1][0]、a[1][1]、a[1][2]和a[1][3],依次类推。
C语言允许构造多维数组,其定义方式和二维数组类似,例如定义如下:
intb[3][4][5];
请根据二维数组的知识推出该三位数组的长度、所包含的元素及在内存中存放顺序。
二、二维数组元素的引用
二维数组元素的引用格式为:
数组名[行下标][列下标]
下标可以是整型常量或整型表达式。
a[1][2]、b[3-1][2*2-1]
可以像使用简单变量一样使用二维数组中的元素。
例7.5二维数组元素的引用。
inti,j,a[3][4];
3;
i++)/*用一个双重循环对二维数组各个元素赋值*/
4;
j++)
a[i][j]=i*2+j;
i++)/*将二维数组输出*/
printf("
a[i][j]);
0123
2345
4567
同样需要注意的是,如果下标越界,编译器并不会发现该错误。
所以在引用二维数组元素时,请确保下标必须在已定义范围内。
例如上例中数组a[3][4],可用的行下标最大值为2,列下标最大值为3,即最后一个元素是a[2][3]。
三、二维数组的初始化
二维数组初始化的方法分为两种:
分行赋初值和连续赋初值。
(1)分行赋初值。
inta[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
根据前面对数组a的理解,二维数组a是由三个一维数组构成的,{1,2,3,4}是对a[0]进行赋值,即给二维数组的第一行元素赋初值,{5,6,7,8}是对a[1]进行赋值,即给二维数组的第二行元素赋初值,依次进行赋值。
可以只对部分元素赋初值,例如:
inta[3][4]={{1,2,3},{5,6,7,8},{9,10}};
没有被赋初值的元素系统默认为0。
所以上述定义等价于:
inta[3][4]={{1,2,3,0},{5,6,7,8},{9,10,0,0}};
如果某行初值的个数大于列数,编译器同样会提示错误信息“toomanyinitializers”,该行的数值不会影响到下一行赋值。
无论是对所有元素赋初值还是对部分元素赋初值,该方法下都可以省略第一维的长度,因为内层花括号的个数可以确定第一维长度,但两种情况下第二维的长度都不能省略。
(2)连续赋初值。
inta[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
这种方式省略了内层花括号,按顺序对数组元素依次赋值。
如果花括号内初值个数小于数组长度,按先后顺序逐行赋值后,没有赋值的元素初始化默认为0。
inta[3][4]={1,2,3,4,5,6,7};
等价于:
inta[3][4]={1,2,3,4,5,6,7,0,0,0,0,0};
该方法下如果对全部元素赋初值,也可省略第一维的长度,系统根据初值个数和第二维的长度来确定第一维长度,同样第二维的长度不能省略。
上述两种初始化的方法推荐第一种,比较直观,易于数据检查。
四、二维数组程序举例
例7.6求一个4×
4矩阵主对角线上元素之和。
例如有如下矩阵a:
主对角线上的元素分别是a[0][0]、a[1][1]、a[2][2]、a[3][3],即8、5、6、9。
#defineN4
inti,j,a[N][N],sum=0;
PleaseinputNarrayelements:
i++)/*输入矩阵元素*/
scanf("
a[i][j]);
Thearrayis:
i++)/*输出矩阵*/
i++)/*求主对角线上元素之和*/
sum+=a[i][i];
Thesumofmaindiagonalelementsis%d\n"
sum);
8645753119645389↙
8645
7531
1964
5389
Thesumofmaindiagonalelementsis28
请思考:
如果要输出副对角线上的元素a[0][3]、a[1][2]、a[2][1]、a[3][0]之和,程序应如何修改?
例7.7统计3个学生,每个学生4门课程的考试成绩,要求输出每个学生的总成绩,每个学生的平均成绩及每门课程的平均成绩。
#include"
stdio.h"
floats[3][4],sum=0;
floatt[3],sa[3],ca[4];
inti,j;
i++)/*输入三个学生的4门课程考试成绩*/
j++)
%f"
s[i][j]);
{
t[i]=0;
t[i]+=s[i][j];
/*t[i]存放第i个学生的4门课程成绩*/
Thesumofstudent%dis:
%-6.2f\n"
i,t[i]);
sa[i]=t[i]/4;
/*a[i]存放第i个学生的4门课程平均成绩*/
Theaverageofstudent%dis:
i,sa[i]);
for(i=0;
ca[i]=0;
for(j=0;
ca[i]+=s[j][i];
/*计算每门课程的总成绩*/
Theaverageofcourse%dis:
i,ca[i]/3);
}
67707169878380