数组

数组简介

我们经常需要在程序中存储某种类型的大量数据值。例如编写一个程序,追踪一只篮球队的成绩,就要存储一个赛季各场分数和各个球员得分。我们可以使用前面学过的知识为每个分数使用不同的变量。然而,如果一个赛季有非常多的赛事,这会非常频繁,因为有球赛的每个球员都需要许多变量。这显然不理想。

理想情况应该把这些变量集中在一个名称下面,这样就不需要定义多个变量了。

计算平均分的问题

我们通过一个例子来了解数组。这个例子是计算某班学生的平均分数。假设该班有十个学生。计算一组数字的平均值,要将它们全加起来,再除以数字的个数。

#include <stdio.h>

int main(void)
{
    int number = 0;                      /* 存储输入的分数*/
    int count = 10;                      /* 学生的个数 */
    long sum = 0L;                       /* 总分          */
    float average = 0.0f;                /* 平均分*/

    /* 读十个数的平均数 */
    for(int i = 0; i < count; i ++)
    {
        printf("输入一个学生的成绩: ");
        scanf("%d", &number);              /* 读取一个数 */
        sum += number;                     /* 累加到sum*/
    }

    average = (float)sum / count;          /* 计算平均数 */

    printf("\n输入的十个数字的平均值是: %f\n", average);
    return 0;
}Code language: PHP (php)

这个程序只对平均值感兴趣,所以累加输入的数然后输出平均值。如果要将其开发为更复杂的程序,需要输入一些数值,再输出每个人的分数,最后输出平均分,上面的代码就无法实现了,因为 number 每次都被覆盖了,不能再次使用。

如何存储所有的分数,可以声明十个整数,但是这样不能用 for 循环输入,而且必须添加代码逐个读入,太麻烦了。

#include <stdio.h>

int main(void)
{
    int number0 = 0, number1 = 0, number2 = 0, number3 = 0, number4 = 0;
    int number5 = 0, number6 = 0, number7 = 0, number8 = 0, number9 = 0;

    long sum = 0L;          /* Sum of the numbers     */
    float average = 0.0f;   /* Average of the numbers */

    /* Read the ten numbers to be averaged */
    printf("输入一到五个数字,\n");
    printf("每个数字用空格隔开,输入完成按回车键.\n");
    scanf("%d%d%d%d%d", &number0, &number1, &number2, &number3, &number4);
    printf("输入后面五个数字,\n");
    printf("每个数字用空格隔开,输入完成按回车键.\n");
    scanf("%d%d%d%d%d", &number5, &number6, &number7, &number8, &number9);

    /* Now we have the ten numbers, we can calculate the average */
    sum = number0 + number1 + number2 + number3 + number4 +
          number5 + number6 + number7 + number8 + number9;
    average = (float)sum / 10.0f;

    printf("\n平均数是: %f\n", average);
    return 0;
}Code language: PHP (php)

数组的概念

数组是一组数目固定、类型相同的数据项,数组中的数据项称为元素。数组中的元素都是 int、long 或者其他类型。

下面是数组声明,非常类似于声明一个含有单一数值的正常变量,但要在名称后面的方括号中放置一个数:

long numbers[10];Code language: CSS (css)

方括号中的数字定义了要存放在数组中的元素个数,称为数组维。存储在数组中的每个数据项都用相同的名称访问,在这个例子中,该名称就是 numbers。要选择某个元素,可以在数组名称后的方括号内使用索引值。索引值从 0 开始,最后一个是 9。所以第一个元素就是 numbers[0],第二个是 numbers[1],最后一个是 numbers[9]。

索引值从 0 开始,不是 1。这一点大家一定要牢记。很多初学者在这个地方犯错。

如果访问的时候索引超过这个数组的合法范围,比如上面如果访问 numbers[10],这个时候就可能程序不能正常运行。编译器检查不出这种错误,所以程序仍可以编译,但是执行有问题。有些时候甚至会覆盖重要信息,导致电脑锁死,需要重启计算机。所以大家要注意避免这个问题。

此外,定义的时候 long numbers[10] 这个地方的 10 并不是索引的意思,而是表示这个数组有十个元素。这点与直接访问比如 numbers[10] 的 10 是不同的概念。long numbers[10] 是声明,numbers[10] 是操作某个元素,当然在这里会超出了范围。

#include <stdio.h>

int main(void)
{
    long numbers[10];
    printf("%ld", numbers[10]); //访问了第十一个元素 可能会访问到一些垃圾数据

    return 0;
}Code language: PHP (php)

下面使用数组知识来解决平均分的问题:

#include <stdio.h>

int main(void)
{
    int numbers[10];                     /* 声明有十个元素的数组*/
    int count = 10;                      /* 读取数据数量 */
    long sum = 0L;                       /* 存储总数          */
    float average = 0.0f;                /* 平均数*/

    printf("\n输入十个数:\n");       /* 提示输入 */

    /* 读取十个数 */
    for(int i = 0; i < count; i ++)
    {
        printf("%2d> ", i + 1);
        scanf("%d", &numbers[i]);          /* 读取十个数到数组里的十个元素 */
        sum += numbers[i];                 /* 累加 */
    }

    for(int i = 0; i < count; i++)
    {
        printf("\n序号:%d 分数:%d", i + 1, numbers[i]);
    }

    average = (float)sum / count;          /* 计算平均数*/

    printf("\n十个数的平均数是: %f\n", average);
    return 0;
}Code language: PHP (php)

内存地址与寻址运算符

寻址运算符 & 输出其操作数的内存地址。前面使用了寻址运算符 &,它广泛用在 scanf() 函数。& 放在变量前面,scanf 需要用到这个变量的地址,scanf 内部可以利用这个地址去修改对应的变量的值。

#include <stdio.h>

int main(void)
{
    /* 定义一些整数变量 */
    long a = 1L;
    long b = 2L;
    long c = 3L;

    /* 声明一些浮点变量 */
    double d = 4.0;
    double e = 5.0;
    double f = 6.0;

    printf("long类型的变量占用%u个字节。", sizeof(long));
    printf("\n这是一些long类型变量的地址:");
    printf("\na的地址是:%p,\nb的地址是:%p", &a, &b);
    printf("\nc的地址是:%p", &c);
    printf("\n\ndouble类型的变量占用%d字节。", sizeof(double));
    printf("\n这是一些double类型变量的地址:");
    printf("\nd的地址是:%p,\ne的地址是:%p", &d, &e);
    printf("\nf的地址是:%p\n", &f);
    return 0;
}Code language: PHP (php)

%u 是显示 sizeof 生成的值,它是无符号的整数。%p 是一个新的格式说明符,用来输出一个内存地址,其值为十六进制。内存地址一般是 32 位或者 64 位,地址的大小取决于引用的最大内存量。

声明一个包含 4 个元素的数组:

long number[4];Code language: CSS (css)

获取数组元素地址的方式类似普通变量:

#include <stdio.h>

int main(void)
{
    int value = 0;
    printf("\n%p", &value);

    long number[4];
    printf("\n%p\n", &number[2]);

    int data[5];
    for(int i = 0; i < 5; i++)
    {
        data[i] = 12 * (i + 1);
        printf("data[%d] 地址是:%p 值是:%d\n", i, &data[i], data[i]);
    }

    return 0;
}Code language: PHP (php)

数组初始化

为了安全起见,预先确定元素的初始值。可以使用大括号并且用逗号隔开,来给数组的元素初始化:

double values[5] = {1.5, 2.5, 3.5, 4.5, 5.5};

前三个元素的值就是 1.5、2.5、3.5,后面两个元素初始化值为 0。

如果没有给元素提供初值,编译器会给它们提供初值 0,所以初值提供了一种把整个数组初始化为 0 的简单方式,只需要给一个元素提供 0:

double values[5] = {0.0};

这样整个数组都初始化为 0.0。

如果初始值超过元素个数,有些编译器就会报错。

可以不指定数组的大小,让编译器从该列值中推断出元素的个数:

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

以上数组的长度就是 10。

#include <stdio.h>

int main(void)
{
    double numbers[] = {0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5};
    printf("%.2lf\n", numbers[3]);

    return 0;
}Code language: PHP (php)

sizeof 运算符

sizeof 可以计算指定类型的变量所占的字节数:

printf("%zu", sizeof(long));Code language: JavaScript (javascript)

sizeof 也可以用于数组:

double values[5] = {1.5, 2.5, 3.5, 4.5, 5.5};
printf("%zu", sizeof values);Code language: JavaScript (javascript)

也可以使用表达式 sizeof values[0] 来计算数组中一个元素所占的字节数。可以通过如下运算来求出数组中的元素的数目:

size_t elementCount = sizeof values / sizeof values[0];

在使用循环处理中的所有元素时,可以使用 sizeof:

#include <stdio.h>

int main(void)
{
    printf("%zu\n", sizeof(double));
    double numbers[] = {0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5};
    printf("%zu\n", sizeof numbers);
    printf("数组的元素个数:%u", sizeof numbers / sizeof(double));

    for(int i = 0; i < sizeof numbers / sizeof(double); i++)
    {
        printf("\n%.1lf", numbers[i]);
    }
    return 0;
}Code language: PHP (php)

多维数组

一个数组里面可以存储多个相同类型的数据,这个我们叫做一维数组。比如 float arr[5]。下面介绍二维数组:

float carrots[25][50];
float numbers[3][5];Code language: CSS (css)

虽然在内存中存储实际上还是顺序存储在内存中,但是我们可以把它想象成三行五列的表格。

二维数组的初始化类似于一维数组,区别是把每一行的初始值放在大括号中 {},再把所有行放在一堆大括号中。如果指定的初始值少于一行的元素,这些值会从每行的第一个元素开始,依序赋予各个元素,剩下未指定初值的元素则初始化为 0。

int numbers[3][4] = {0};

三维数组可以理解为我们有四块地,每块地 10 行 20 列:

double beans[4][10][20];Code language: CSS (css)

要处理多维数组中的所有元素,需要一个嵌套循环,嵌套的层数就是数组的维数:

#include <stdio.h>

int main(void)
{
    int numbers3[3][3][5] =
    {
        {
            {1, 2, 3, 4, 5},
            {1, 2, 3, 4, 5},
            {1, 2, 3, 4, 5}
        },
        {
            {1, 2, 3, 4, 5},
            {1, 2, 3, 4, 5},
            {1, 2, 3, 4, 5}
        },
        {
            {1, 2, 3, 4, 5},
            {1, 2, 3, 4, 5},
            {1, 2, 3, 4, 5}
        }
    };

    int sum = 0;
    for(int i = 0; i < 3; i++)
    {
        for(int j = 0; j < 3; j++)
        {
            for(int k = 0; k < 5; k++)
            {
                sum += numbers3[i][j][k];
            }
        }
    }

    printf("%d", sum);
    return 0;
}Code language: PHP (php)

使用 sizeof 可以确定多维数组中每一维的元素个数:

#include <stdio.h>

int main(void)
{
    int numbers3[3][3][5] =
    {
        {
            {1, 2, 3, 4, 5},
            {1, 2, 3, 4, 5},
            {1, 2, 3, 4, 5}
        },
        {
            {1, 2, 3, 4, 5},
            {1, 2, 3, 4, 5},
            {1, 2, 3, 4, 5}
        },
        {
            {1, 2, 3, 4, 5},
            {1, 2, 3, 4, 5},
            {1, 2, 3, 4, 5}
        }
    };

    int sum = 0;
    for(int i = 0; i < sizeof(numbers3) / sizeof(numbers3[0]); i++)
    {
        for(int j = 0; j < sizeof(numbers3[0]) / sizeof(numbers3[0][0]); j++)
        {
            for(int k = 0; k < sizeof(numbers3[0][0]) / sizeof(numbers3[0][0][0]); k++)
            {
                sum += numbers3[i][j][k];
            }
        }
    }

    printf("%d", sum);
    return 0;
}Code language: PHP (php)

变长数组

前面的所有数组都在代码中指定了固定的长度,也可以定义其长度在程序运行期间确定。

#include <stdio.h>

int main(void)
{
    size_t len = 0;
    printf("请输入数组长度:");
    scanf("%zd", &len);
    float numbers[len];

    numbers[3] = 1.2f;
    printf("%f", numbers[3]);

    return 0;
}Code language: PHP (php)

也可以在执行期间确定二维或多维数组:

#include <stdio.h>

int main(void)
{
    size_t len = 0;
    printf("请输入数组长度:");
    scanf("%zd", &len);

    size_t len2 = 0;
    printf("请输入数组长度:");
    scanf("%zd", &len2);

    float numbers[len][len2];

    numbers[3][2] = 1.2f;
    printf("%f", numbers[3][2]);

    return 0;
}Code language: PHP (php)

下面的代码可以检查编译器是否支持变长数组:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    #ifdef __STDC_NO_VLA__
        printf("不支持");
        exit(1);
    #endif

    return 0;
}Code language: PHP (php)

使用变长数组计算平均分:

#include <stdio.h>

int main(void)
{
    size_t len = 0;
    printf("请输入学生个数:");
    scanf("%zd", &len);

    int grades[len];
    long sum = 0L;
    float average = 0.0f;

    printf("输入学生分数\n");

    for(size_t i = 0; i < len; ++i)
    {
        printf("%2zd>", i + 1);
        scanf("%d", &grades[i]);
        sum += grades[i];
    }

    printf("分数\n");

    for(size_t i = 0; i < len; ++i)
    {
        printf("grades[%2zd]=%3d", i, grades[i]);

        if((i + 1) % 5 == 0)
            printf("\n");
    }

    average = (float)sum / len;
    printf("\n %zd 个学生的平均分是 : %.2f\n", len, average);

    return 0;
}Code language: PHP (php)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注