一. 一维数组
1.数组作用:数组通常被用来储存程序需要的数据。
2.构成:数组由数据类型相同的一系列元素组成。
3.数组的定义和使用: 定义数组:<类型>变量名称[元素数量] 元素数量必须为整数 需要使用数组时,通过声明数组告诉编译器数组中内含多少元素和这些元素的类型。编译器根据 这些信息正确地创建数组。普通变量可以使用的类型,数组元素都可以用。
/* 数组声明*/
int main(void)
{
float candy[365]; /* 内含365个float类型元素的数组 */
char code[12]; /*内含12个char类型元素的数组*/
int states[50]; /*内含50个int类型元素的数组 */
...
}
//方括号([])表明candy、code和states都是数组,方括号中的数字表明 数组中的元素个数。
4.如何访问数组:要访问数组中的元素,通过使用数组下标数(也称为索引)表示数组中 的各元素。数组元素的编号从0开始,所以candy[0]表示candy数组的第1个元素,candy[364]表示第365个元素,也就是最后一个元素。
5.初始化数组:
int main(void)
{
int powers[8] = {1,2,4,6,8,16,32,64}; /* 从ANSI C开始支持这种初始化 */
...
}
/* day_mon1.c -- 打印每个月的天数 */
#include <stdio.h>
#define MONTHS 12
int main(void)
{
int days[MONTHS] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
int index;
for (index = 0; index < MONTHS; index++) {
printf("Month %2d has %2d days.\n", index + 1, days[index]); }
return 0;
}
/*该程序的输出如下:
Month 1 has 31 days.
Month 2 has 28 days.
Month 3 has 31 days.
Month 4 has 30 days.
Month 5 has 31 days.
Month 6 has 30 days.
Month 7 has 31 days.
Month 8 has 31 days.
Month 9 has 30 days.
Month 10 has 31 days.
Month 11 has 30 days.
Month 12 has 31 days.*/
#define MONTHS 12 称为宏定义
牢记:数组元素的编号从0开始,超出称为数组下标越界!!!
注意:如果部分初始化数组,剩余的元素就会被初始化为0。如果初始化列表的项数多于数组元素个数,则编译器会视为错误。
eg: 演示程序
/* day_mon2.c -- 让编译器计算元素个数 */
#include <stdio.h>
int main(void)
{
const int days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31 };
int index;
for (index = 0; index < sizeof days / sizeof days[0]; index++) //index是数组下标的意思
printf("Month %2d has %d days.\n", index + 1, days[index]);
return 0;
}
程序解释:sizeof不是变量而是运算符,该运算符给出它的运算对象的大小(以字节为单位)。所以sizeof days是整个数组的大小(以字节为单位),sizeof day[0]是数组中第一个元素的大小(以字节为单位)。整个数组的大小除以单个元素的大小就是数组元素的个数。
8.如何给数组元素赋值:声明数组后,可以借助数组下标(或索引)给数组元素赋值。例如,下面的程序段给数组的所有元素赋值:
/* 给数组的元素赋值 */
#include <stdio.h>
#define SIZE 50
int main(void)
{
int counter, evens[SIZE];
for (counter = 0; counter < SIZE; counter++)
evens[counter] = 2 * counter;
...
}
注意: 这段代码中使用循环给数组的元素依次赋值。 C 不允许把数组作为一个单元赋给另一个数组,除初始化以外也不允许使用花括号列表的形式赋值。
/* 一些无效的数组赋值 */
#include <stdio.h>
#define SIZE 5
int main(void)
{
int oxen[SIZE] = {5,3,2,8}; /* 初始化没问题 */
int yaks[SIZE]; yaks = oxen; /* 不允许 */
yaks[SIZE] = oxen[SIZE]; /* 数组下标越界 */
yaks[SIZE] = {5,3,2,8}; /* 不起作用 */
···
}
注意数组下标越界:oxen数组的最后一个元素是oxen[SIZE-1],所以oxen[SIZE]和yaks[SIZE] 都超出了两个数组的末尾。
9.指定数组大小:
int n = 5;
int m = 8; float a1[5]; // 可以
float a2[5*2 + 1]; //可以
float a3[sizeof(int) + 1]; //可以
float a4[-4]; // 不可以,数组大小必须大于0
float a5[0]; // 不可以,数组大小必须大于0
float a6[2.5]; // 不可以,数组大小必须是整数
float a7[(int)2.5]; // 可以,已被强制转换为整型常量
float a8[n]; // C99之前不允许
float a9[m]; // C99之前不允许
数组元素引用时,应注意以下问题:
- 下标表达式的值必须是一个整型的量,可以是整型常量、整型变量或运算结果为整型的表达式。
- 只能单个使用数组中的元素,而不能引用整个数组(字符数组除外)。
- C语言的数组下标是从0开始的,因此数组的下标范围为0~L-1,其中L为数组的长度。C语言中不对数组进行越界检查。数组a包含了6个元素,若有语句a[10]=2;,编译时不出错,但可能导致其他变量或程序出错。
- 数组里所有的元素具有相同的数据类型。
- 一旦创建,不能改变大小。
- 数组中的元素在内存中是紧密连续排列的。
-
flizny == &flizny[0]; // 数组名是该数组首元素的地址flizny 和 &flizny[0] 都表示数组首元素的内存地址( & 是地址运算符)。
二.二维数组
1.二维数组类型说明的一般格式:类型说明符 数组名[常量表达式1][常量表达式2];其中,常量表达式1用来定义数组的行数,常量表达式2用来定义了二维数组x的列数。
如定义的二维数组x,由3行3列9个元素组成。元素的行、列下标都从0开始。这9个元素分别为
x[0] [0] x[0] [1] x[0] [2]
x[1] [0] ×[1] [1] x[1] [2] x[2] [0] x[2] [1] x[2] [2]
2.二维数组的初始化: <1.给全部元素赋初值
(1)按行赋初值。例如:int a[2] [3]={(1,2,3),(4,5,6)};
(2)按存储结构赋值,所有数据放在一个花括号中。 例如:int a[2][3]={1,2,3,4,5,6}; /★与按行赋值等效★/
<2.给部分元素赋初值,未赋值元素的值为0
(1)按行赋初值。例如:int a[2][3]={(1, 2, 3),(4)};则a[1][1], a[1][2]的值为0。 (2)按存储结构赋值,所有数据放在一个花括号中。例如:int al2][3]=(1,2,3,4); 同样a[1][1], a[1][2]的值为0。
<3.省略二维数组的行数,二维数组的列数不能省略 (1)按行赋初值。例如:int a[ ][3]={(1,2,3),(4)};系统自动定义数组a的行数为2。 (2)按存储结构眼值,数组的行数为花括号中元素的个数除以教组的列数向上取整。 例如:int a[ ][3]={1,2,3,4};花括号中包含4个元素,用4/3向上取整,则系统自动定义数组a的行 数为2。
3.二维数组的引用:定义了二维数组以后,可以用下列格式引用二维数组中的每个元素。 数组名[下标][下标] 例如:
int c[3] [2];
c[1] [1]=2;
c[1] [2]=C[1] [1]*2;
演示一个较为复杂的代码
/* rain.c -- 计算每年的总降水量、年平均降水量和5年中每月的平均降 水量 */
#include <stdio.h>
#define MONTHS 12 // 一年的月份数
#define YEARS 5 // 年数
int main(void)
{// 用2010~2014年的降水量数据初始化数组
const float rain[YEARS][MONTHS] = {
{ 4.3, 4.3, 4.3, 3.0, 2.0, 1.2, 0.2, 0.2, 0.4, 2.4, 3.5, 6.6 },
{ 8.5, 8.2, 1.2, 1.6, 2.4, 0.0, 5.2, 0.9, 0.3, 0.9, 1.4, 7.3 },
{ 9.1, 8.5, 6.7, 4.3, 2.1, 0.8, 0.2, 0.2, 1.1, 2.3, 6.1, 8.4 },
{ 7.2, 9.9, 8.4, 3.3, 1.2, 0.8, 0.4, 0.0, 0.6, 1.7, 4.3, 6.2 },
{ 7.6, 5.6, 3.8, 2.8, 3.8, 0.2, 0.0, 0.0, 0.0, 1.3, 2.6, 5.2 }};
int year, month;
float subtot, total;
printf(" YEAR RAINFALL (inches)\n");
for (year = 0, total = 0; year < YEARS; year++)
{ // 每一年,各月的降水量总和
for (month = 0, subtot = 0; month < MONTHS; month++) subtot += rain[year][month];
printf("%5d %15.1f\n", 2010 + year, subtot);
total += subtot; // 5年的总降水量
}
printf("\nThe yearly average is %.1f inches.\n\n", total / YEARS);
printf("MONTHLY AVERAGES:\n\n");
printf(" Jan Feb Mar Apr May Jun Jul Aug Sep Oct ");
printf(" Nov Dec\n");
for (month = 0; month < MONTHS; month++)
{ // 每个月,5年的总降水量
for (year = 0, subtot = 0; year < YEARS; year++) subtot += rain[year][month];
printf("%4.1f ", subtot / YEARS); }printf("\n"); 662
return 0;
}
/*下面是该程序的输出:
YEAR RAINFALL (inches)
2010 32.4
2011 37.9
2012 49.8
2013 44.0
2014 32.9
The yearly average is 39.4 inches. MONTHLY AVERAGES:
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
7.3 7.3 4.9 3.0 2.3 0.6 1.2 0.3 0.5 1.7 3.6 6.7 */
for (year = 0, total = 0; year < YEARS; year++)
{ // 处理每一年的数据
for (month = 0, subtot = 0; month < MONTHS; month++)
...// 处理每月的数据
...//处理每一年的数据
}
第2个嵌套for循环和第1个的结构相同,但是内层循环遍历year,外层循 环遍历month。记住,每执行一次外层循环,就完整遍历一次内层循环。因此,在改变月份之前,先遍历完年,得到某月 5 年间的平均降水量,以此类推:
for (month = 0; month < MONTHS; month++)
{ // 处理每月的数据
for (year = 0, subtot =0; year < YEARS; year++)
...// 处理每年的数据
...// 处理每月的数据
}