Skip to content

第五部分:数组


用我们之前学到的C语言语法,如果想要计算 30 个学生的平均成绩,我们需要定义 30 个 float 型变量,然后将它们加起来除以 30 就可以了。30 个变量的问题似乎不大,只需要定义 30 个变量(s1s2s3……s30)即可。换作是 1000 个变量怎么办呢?人们想了一个办法,就是用同一个名字代表具有相同属性的一群数据,再辅以下标来在它们彼此间区分他们。例如,S1S2S3代表学生 1、学生 2、学生 3。一群具有同属性的数据在一起称为“数组”。数组是一组有序数据的集合。

数组中各数据的排列是有一定规律的,下标代表数据在数组中的序号。用一个数组名和一个下标的组合(如 a2)来确定数组中唯一的元素,同样可以这样赋予数组实际意义。例如:S12 就可以代表第 12 位同学的成绩。数组中的每一个元素都属于同一个数据类型,不能把不是同一个类型的数据放在同一个数组中。

一、一维数组

1.一维数组的定义与引用

由于在编写程序的时候无法输入下标,所以在 C 语言中用数字加方括号表示下标。如下即是对一个一维数组的定义:

c
int a[10];

它表示定义了一个一维数组,数组名为 a,这个数组有 10 个整形元素。定义一个一维数组的一般形式为:

类型标识符 数组名[常量表达式]

需要注意的是,按照 int a[10] 定义后这个数组中有十个元素,从 a[0] 开始,到 a[9] 结束,并没有 a[10] 这个元素。“常量表达式”可以为常量与符号常量,如 a[3 + 5],但不可以是 a[n],也就是说,不允许对数组做动态定义。例如:

c
int x;
scanf("%d", &x);
int a[x];

这种定义在 C 语言中是不允许的。

经过 int a[10] 的定义,内存中为数组 a 划分出一片存储空间,这片存储空间是连续的,可以存放 10 个整形元素。定义 10 个元素的数组的时候相当于定义了 10 个整形变量,显然比章首提到的方法方便。

当我们想要使用数组中的元素时,可以用:

数组名[下标]

的形式来引用数组中的任意一个元素。例如我们想要对 10 个元素依次进行赋值,然后按倒序输出:

c
#include<stdio.h>
int main()
{
    int i, a[10];
    for(i = 0; i < 10; i ++)
        a[i] = i;
    for(i = 9; i >= 0; i --)
        printf("%d", a[i]);
    printf("\n");
    return 0;
}

2.一维数组的初始化

在定义变量的同时,对数组元素赋值,称为数组的初始化。对一维数组初始化的方法有以下几种:

a.定义数组时给全部元素赋值

c
int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

此外,由于给全部元素赋值时长度已经确定,可以不指定数组长度。上面的代码等价于下面这句代码:

c
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

使用这种方法定义数组时,需要格外注意的是数组名后面的一对方括号不能省略,方括号是数组的标识符。

b.定义数组时给部分元素赋值

c
int a[10] = {0, 1, 2, 3, 4};

这样的话,数组 a 前五个元素这样赋值,其余元素初值为 0

c.使数组元素全部为 0

用下面的方法可以将数组元素全部赋初值为0:

c
int a[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

或者是:

c
int a[10] = {0};

定义数组时应当指定数组长度并进行初始化,如果没有进行初始化,系统会自动将其初始化为 0。如果是字符型数组则初始化为 '\0',如果是指针数组则初始化为 NULL

3.应用举例

将数组中的元素按顺序重新存放。

在这里介绍“起泡排序法”,这种方法的基本思路是,将相邻的两个数比较,将较小的数放在前面。例如有 6 个数字:985420,要求按从小到大的顺序排好。最开始数字是这样排序的:

9  8  5  4  2  0

我们第一趟将最顶端的数字 9 依次与后面的数字比较,比 9 大的放在右面,比 9 小的就与9交换位置。第一次 ~ 第六次的比较过程如下:(下划线表示交换位置)

*9  8*  5  4  2  0
8  *9  5*  4  2  0 
8  5  *9  4*  2  0 
8  5  4  *9  2*  0
8  5  4  2  *9  0*
8  5  4  2  0  9

可以看到,经过 6 次比较,原本在最顶端的数字 9 已经沉底,第二趟我们进行如下过程的比较:

*8  5*  4  2  0  9
5  *8  4*  2  0  9
5  4  *8  2*  0  9
5  4  2  *8  0*  9
5  4  2  0  8  9

第二趟中,原本沉底的数字 9 是不动的,在 9 沉底后在顶端的数字 8 经过第二趟比较,也沉在了仅次于 9 的底端。以此来推,经过 5 趟比较,就可以将原本的 6 个数字按照升序排好。可以分析出来,当有 n 个数时,需要进行 n – 1 趟比较。上述算法我们称作“起泡排序法”,算法的程序如下:(假设有 10 个数据)

c
#include<stdio.h>
int main()
{
    int a[10];
    int i, j, t;
    printf("Input  10  numbers:\n");
    for(i = 0 ; i < 10 ; i ++)
        scanf("%d" , &a[i]);
    printf("\n");
    //起泡排序开始
    for(j = 0; j < 9; j ++)
        for(i = 0; i < 9 – j; i ++)
        {
            t = a[i];
            a[i] = a[i + 1];
            a[i + 1] = t;
        }
    //起泡排序结束
    printf("The sorted numbers are:\n");
    for(i = 0 ;i < 10; i ++)
        printf("%d\t" , a[i]);
    printf("\n");
    return 0;
}

二、二维数组与引申

当我们有一个表格,记录运动员各项比赛得分情况,横排是运动员姓名,竖排是项目名称,如下:

高老大王二张三李四赵五钱六
100米跑957589759887
铅球859188929870
标枪758595987682

我们可以将这个表格的数据存放在二维数组中,例如我们可以用 S[1][2] 代表王二的铅球成绩,即 S[1][2] = 91。二维数组也称为矩阵,这样的数组具有两个维度:行和列。我们可以用类似定义一维数组的方式来定义一个二维数组:

c
int a[3][3]

这样我们就定义了一个 3 x 3 的二维数组,写成矩阵形式如下:

a[0][0]	a[0][1]	a[0][2]
a[1][0]	a[1][1]	a[1][2]
a[2][0]	a[2][1]	a[2][2]

仍然需要注意的是,二维数组的两个维度标号都是从 0 开始的,这一点与一维数组相同。需要明确的概念是,二维数组写成矩阵形式是为了理解方便,但是在内存中,二维数组仍然是线性存储的。

对二维数组进行初始化时与一维数组有所不同,例如我们对上述的 3 x 3 数组进行初始化:

c
int a[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};

也可以将所有元素写在一个花括号内:

c
int a[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9};

同样我们也可以对部分元素赋初值,这个时候其余元素为 0

c
int a[3][3] = {{1}, {2, 3}, {4}};

赋值结果是:

1	0	0
2	3	0
4	0	0

对二维数组进行赋初值时,要记得一个准则,那就是在一个花括号内的是同一行,隔一个花括号就是下一行。

以上就是二维数组的定义与调用方式,下面举一个例子来讲解二维数组的综合运用。例如我们需要将一个二维数组行列互换,存在另一个二维数组中。首先我们分析问题,这个问题的中心就在于将二维数组 a 的元素 a[i][j] 存放在另外一个二维数组 b[j][i] 中。假设有二维数组 a[2][3],写程序如下:

c
#include<stdio.h>
int  main()
{
    int i, j;
    int a[2][3] = {0};
    for(i = 0; i <= 1; i ++)
        for(j = 0; j <=2; j ++)
            scanf("%d", &a[i][j]);
    int b[3][2] = {0};
    printf("a:\n");
    for(i = 0; i <= 1; i ++)
    {
        for(j = 0; j <=2; j ++)
        {
            printf("%d", a[i][j]);
            b[j][i] = a[i][j];
        }
        printf("\n");
    }
    printf("b:\n");
    for(i = 0; i <= 2; i ++)
    {
        for(j = 0; j <= 1; j ++)
            printf("%d", b[i][j]);
        printf("\n");
    }
    return 0;
}

二维数组的定义与调用方法可以引申为多维数组,只要有需要,数组可以有三维、四维……

三、字符数组

C 语言中不存在字符串类型,字符串是存放在字符数组中。用来存放字符的数组是字符数组,我们可以像定义一维数组那样来定义一个字符数组:

c
char a[10];

这样我们定义了一个可以存放十个字符的数组a。由于字符是以整数形式存储,因此我们也可以用整形的数组存放字符,例如:

c
int a[10];
a[0] = 'a';

这样是合法的,但浪费存储空间。因为一个 int 型的存储空间是4字节,但一个字符只占用 1 字节,剩下的 3 字节是浪费的。

对字符数组进行初始化的方法也有很多种,其中最容易理解的方法是:

c
char a[10] = {'W', 'E', 'L', 'C', 'O', 'M', 'E'};

这个时候,数组 a 的存放状态如下:

W	E	L	C	O	M	E	\0	\0	\0

需要提到的是,在字符数组中,最后一位一定以空字符('\0')结束,作为字符数组结束的标志。除此之外,我们还有另外一种初始化字符数组的方法:

c
char a[] = "WELCOME";

用这种方法定义字符数组,是直接把字符串赋值给数组,字符串用双撇号括在一起。当用 printf 输出字符数组时,检查到第一个 '\0' 时即停止输出。当我们对字符数组进行输入输出时,可以对数组的某个元素进行输入输出,也可以对整个数组进行输出。例如:

c
char a[] = "BOY";
printf("%s" , a);    //%s意为以字符串形式输出/输入
printf("%c" , a[0]);    //用%c输出/输入单个元素

此外,C 语言还提供了一些字符串处理函数:

函数名与调用方式作用
puts(字符数组);将一个字符串输出到屏幕上
gets(字符数组);从输入终端获取一个字符串
strcat(字符数组1,字符数组2);将字符串2接在字符串1的后面
strcpy(字符数组1,字符串2);将字符串2赋值到字符串1里
strcmp(字符串1,字符串2);字符串比较大小
strlen(字符串);返回字符串的长度
srtlwr(字符串); strupr(字符串);将字符串转换为大写/小写

当使用这些函数时,要在预处理部分将 string.h 头文件包含在程序中。

四、练习题

函数部分的习题并在这里。

  1. 写一个函数可以将两个字符串连接在一起,不用 stract 函数。

  2. 求一个 3 x 3 整形矩阵对角线数字之和。

  3. 有一篇英文文章,共 3 行,每行 80 个字符。要求统计出其中英文大写字母、小写字母、数字、空格以及其他字符的个数。

  4. 写一个函数,可以给 3 x 3 的数组转置。

  5. 写一个函数,可以将一个字符串倒序存放。

© thebestxt.cc
辽ICP备16009524号-8
本站所有文章版权所有,转载请注明出处