参考资料:
高教版《全国计算机等级考试二级教程——C语言程序设计》
《21天学通C语言》
数组的基本概念
数组是一组带索引的数据存储位置,各位置的名称相同,以不同的下标或索引来区分。下标(也叫作索引)指的是数组变量名后面方括号中的数字。数组中的每个存储位置被称为数组元素。
与其他C语言的变量类似,在使用数组前必须声明它。
一维数组的定义和一维数组元素的引用
一维数组的定义
当数组中的每个元素只带有一个下标时,称这样的数组为一维数组。
在C语言中,定义一维数组的语句一般形式如下:
类型名 数组名[整型常量表达式],……;
例如:
int a[8];
在这里,int是类型名,a[8]就是一维数组说明符。
以上语句说明了以下几点:
- 定义了一个名为a的一维数组。
- 方括号中的8规定了a数组含有8个元素,它们是a[0],a[1],a[2]……
- 类型名int规定了a数组中每个元素都是整型,在每个元素中只能存放整型数。
- 每个元素只有一个下标。每个数组中第一个元素的下标总为0(称为数组下标的下界),因此,以上a数组中的最后一个元素的下标是7(称为数组下标的上界)。
- C编译程序将为a数组在内存中开辟名为a[0]-a[7]的8个连续的存储单元,可以用这些名字来直接引用各存储单元。
在一个定义数组的语句中,可以有多个数组说明符,它们之间用逗号隔开,如:
double w[22], v[100], u[5];
数组说明符和普通变量名可同时出现在一个类型定义语句中,如:
char c1, c2, carr[81];
注意:数组说明符的一对方括号中只能是整型常量或整型常量表达式。
声明数组时,可以用字面常量或通过#define创建的符号常量来指定元素的个数,因此以下两种声明方式等价:
#define MONTHS 12
int array[MONTHS];
int array[12];
注意:大部分编辑器都不允许用const关键字创建的符号常量来声明数组的元素。
以下语句在编译时会报错:
const int MONTHS = 12;
int array[MONTHS];
一维数组元素的引用
一维数组的引用形式如下:
数组名 [下标表达式]
例如,若有以下定义语句:
double x[8];
则x[0],x[j],x[i + k]都是对x数组中元素的合法引用形式。
- 一个数组元素实质上就是一个变量名,代表内存中的一个存储单元。
- 一个数组占有一串连续的存储单元。
- 在C语言中,一个数组不能整体引用。数组名中存放的是一个地址常量,它代表整个数组的首地址。
一维数组的初始化
一维数组初始化的形式为:
int a[8] = {0,1,2,3,4,5,6,7};
以上语句给a[0]赋初值0,给a[1]赋初值1……给a[7]赋初值7。
当所赋初值少于所定义数组元素的个数时,将自动给后面的元素补以初值0。
对于字符型数组也同样补以初值0。例如,以下两个定义相等:
char c[5] = {'@'};
char c[5] = {'@', '\0', '\0', '\0', '\0'};
当所赋初值多与所定义数组元素的个数时,在编译时会报错。
通过赋初值定义数组的大小
C语言规定可以通过赋初值来定义数组的大小,这时数组说明符的一对方括号中可以不指定数组的大小。例如,以下两条语句等价:
int a[] = {0, 0, 0, 0, 0};
int a[5] = {0};
一维数组的使用
数组元素可用于程序中任何相同类型的非数组变量的地方,通过使用数组名和带方括号的数组下标来访问数组中的各元素。
例如,以下语句将89.95存储在第2个数组元素中:
expenses[1] = 89.95;
下面的语句将数组元素expenses[11]中存储的值的副本赋给expenses[10]数组元素:
expenses[10] = expenses[11];
程序中经常会将整型变量或表达式作为下标,或者甚至是另一个数组元素,例如:
float expenses[100];
int a[10];
//其他语句已省略
expenses[i] = 100; //i是一个整型变量
expenses[2 + 3] = 100; //expenses[2 + 3]相当于expenses[5]
expenses[a[2]] = 100; //a[]是一个数组
如果有一个整型数组a[],其中元素a[2]中存储8,则以下两种写法的效果相同:
expenses[a[2]] = 100;
expenses[8] = 100;
一维数组和指针
一维数组和数组元素的地址
在C语言中,在函数体中或在函数外部定义的数组名可以认为是一个存放地址值的指针变量名,其中的地址值是数组第一个元素的地址,定义数组时的类型即是此指针变量的基类型,数组名是指向数组第一个元素的指针。
注意:这个指针变量中的地址值不可改变,也就是说不可以给数组名重新赋值,可以认为数组名是一个地址常量。
比如,定义:
float a[10], *p, x;
语句a = &x;或a++都是非法的,因为不能给a重新赋地址值。a将永远指向数组的首地址。
但是,可以声明一个指针变量并将其初始化以指向该数组。例如,下面的代码声明并初始化指针变量p_array,把array数组首元素的地址存储在p_array中:
int array[100];
int *p_array = array;
//其他代码已省略
因为p_array是一个指针变量,所以可以修改它的值让它指向别处,或指向array[]的其他元素。
可以用对数组名加一个整数的办法,来依次表达该数组中不同元素的地址。例如:
int k;
for(k = 0; k < 10; k++)
p = a + k;
在循环中并没有改变a的值,但通过表达式a+k,逐一给出了a数组中每个元素的地址,使p依次指向a数组中的每一个元素。
可以通过以下循环从终端读入数据依次放入a数组中:
for(k = 0; k <10; k++)
scanf("%d", a + k);
语句p = &a[0]和p = a都是合法的。这两条语句都使指针变量p指向数组a的首地址。
以下循环语句中,由于在进入循环时使指针变量p指向了a数组的首地址,p++将使指针变量p依次指向a数组中的每一个元素:
for(p = a, k = 0; k < 10; k++)
p++;
下面的两条循环语句也可以从终端读入数据依次放入a数组中,这种操作在一些简单的OJ题目中比较常见:
for(p = a, k = 0; k < 10; k++)
scanf("%d", p++);
for(p = a; p - a < 10; p++)
scanf("%d", p);
例:
以下程序通过声明short、float、double类型的数组并且依次显示数组元素的地址,演示了不同类型数组的元素和地址之间的关系。
#include <stdio.h>
//声明一个计数器变量和三个数组
int chr;
short array_s[10];
float array_f[10];
double array_d[10];
int main(void)
{
//打印表头
printf("\t\tShort\t\tFloat\t\tDouble");
printf("\n============================================================");
//打印各数组元素的地址
for(ctr = 0; ctf < 10; ctr++)
printf("\nElement %d:\t%ld\t%ld\t%ld", ctr, &array_s[ctr], &array_f[ctr], &array_d[ctr]);
printf("\n============================================================");
return 0;
}
输出:
Short Float Double
============================================================
Element 0: 13084064 13084000 13083904
Element 1: 13084066 13084004 13083912
Element 2: 13084068 13084008 13083920
Element 3: 13084070 13084012 13083928
Element 4: 13084072 13084016 13083936
Element 5: 13084074 13084020 13083944
Element 6: 13084076 13084024 13083952
Element 7: 13084078 13084028 13083960
Element 8: 13084080 13084032 13083968
Element 9: 13084082 13084036 13083976
============================================================
可以看出,相邻两个short类型的元素的间隔是2字节,相邻两个float类型的元素的间隔是4字节,相邻两个double类型的元素的间隔是8字节。
通过数组的首地址引用数组元素
a是a数组元素的首地址,a的值等于&a[0],则a + 1的值等于&a[1]……a+9的值等于&a[9]。
可以通过间接访问运算符“*”来引用地址所在的存储单元,因此对于数组元素a[0],可以用表达式*&a[0]来引用,也可以用*(a + 0)或*a来引用。
因此,可以通过以下语句逐个输出a中数组元素的值:
for(k = 0; k < 10; k++)
printf("%4d", *(a + k));
此语句与以下语句等效:
for(k = 0; k < 10; k++)
printf("%4d", a[k]);
通过指针引用一维数组元素
若有:
float a[10], *p, k;
p = a; //或p = &a[0];
可以使用间接访问运算符,通过指针p来引用a数组中的元素。
对于数组元素a[0],可以用表达式*(p + 0),即*p来引用;同理,对于数组元素a[1],可以用表达式*(p + 1)来引用。
因此,可以通过以下语句逐个输出a数组元素的值:
for(p = a, k = 0; k < 10; k++)
printf("%4d", *(p + k));
以上语句与以下语句等效:
for(k = 0; k < 0; k++)
printf("%4d", a[k]);
以上两种写法都没有移动指针p。可以用以下两种方式逐步移动指针p来引用a数组中的每一个元素,逐个输出a数组元素的值:
for(p = a, k = 0; k < 10; k++)
printf("%4d", *p++);
for(p = a; p - a < 10; p ++)
printf("%4d", *p);
用带下标的指针变量来引用一维数组元素
若有:
int *p, s[10], i; //0≤i<10
p = s;
已经知道:
- 可以用&s[i]、s + i和p + i来表示s[i]的地址
- 可以用s[i]、*(s + i)、*(p + i)来表示数组元素s[i]
所以,*(p + i)也可以用p[i]来表示。
事实上,在C语言中,方括号是一种运算符。
因此,当p指向s数组的首地址时,表示数组元素s[i]的表达方式有:
- s[ i ]
- *(s + i)
- *(p + i)
- p[ i ]
注意:s是不可变的,p中的地址是可变的。
函数之间对一维数组和数组元素的引用
数组元素作为实参
调用函数时,数组元素可以作为实参传递给形参,每个数组元素实际上代表内存中的一个存储单元,所以对于的形参必须是类型相同的变量。
数组名作为实参
数组名也可以作为实参传递,但数组名本身是一个地址值,所以对应的形参就应当是一个指针变量,其基类型必须与数组的类型一致。
在函数中,可以通过此指针变量来引用调用函数中对应的数组元素,从而对调用函数中对应的数组元素进行操作二改变其中的值。
例1(高教版《全国计算机等级考试二级教程——C语言程序设计》P117 例9.2):
编写程序,通过一个函数给主函数中定义的数组输入若干大于或等于0的整数,用负数作为输入结束的标志;调用另一个函数输出该数组中的数据。
#include <stdio.h>
#define M 100
void arrout(int *, int); //函数说明语句,此函数用以输出数组中的值
int arrin(int *); //函数说明语句,此函数用以给数组输入数据
int main(void)
{
int s[M], k;
k = arrin(s);
arrout(s, k);
return 0;
}
int arrin(int *a)
{
int i = 0, x;
scanf("%d", &x);
while(x >= 0)
{
*(a + 1) = x;
i++;
scanf("%d", &x);
}
return i;
}
void arrout(int *a, int n)
{
int i;
for(i = 0; i < n; i++)
printf(((i + 1) % 5 == 0) ? "%4d\n" : *(a + 1)); //根据i的值来确定不同的格式串
printf("\n");
}
当数组名作为实参时,对应的形参除了是指针外,还可以有其他形式。上例中arrin函数的首部还可以写成以下形式:
int arrin(int a[])
int arrin(int a[M])
此外,通过指针引用数组元素时,除了可以用上例的*(a + i)外,也可以写出a[ i ]的形式。
数组元素地址作为实参
当用数组元素地址作为实参时,对应的形参也应当是基类型相同的指针变量。
例2(高教版《全国计算机等级考试二级教程——C语言程序设计》P119 例9.3):
编写函数,对具有10个元素的char类型数组,从下标为4的元素开始,全部设置星号“*”,保持前四个元素的内容不变。
#include <stdio.h>
#define M 10
#define B 4
void setstar(char *, int);
void arrout(char *, int);
int main(void)
{
char c[M] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'};
setstar(&c[4], M - B);
arrout(c, M);
return 0;
}
void setstar(char *a, int n)
{
int i;
for(i = 0; i < n; i++)
*(a + i) = '*';
}
void arrout(char *a, int n)
{
int i;
for(i = 0; i < n; i++)
printf("%c", a[i]);
printf("\n");
}
输出:
ABCD******
setstar函数的首部还可以写成以下形式:
void setstar(int a[], int n)
void setstar(int a[M - B], int n)
网友评论