C: 数组

想象一排储物柜,每个柜子编号从0开始,里面存着同类的物品——这就是数组。它让你用一个名字管理一组相同类型的数据。

1. 一维数组的定义与初始化

数组是相同类型元素的有序集合,在内存中连续存放。定义时需要指明元素类型和数量。

C
int scores[5];

上面定义了一个包含5个int元素的数组,但内容未初始化,值是不确定的。建议定义时立即初始化:

C
int scores[5] = {90, 85, 78, 92, 88};

如果初始化列表比数组短,剩余元素自动填0:

C
int data[5] = {10, 20};

此时data[2]data[3]data[4]都是0。利用这个特性,可以将整个数组清零:

C
int zeros[100] = {0};

如果给出了完整初始化列表,可以省略数组长度,编译器会自动计算:

C
int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

2. 下标访问

数组元素通过下标(索引)访问,下标从0开始。对于长度为N的数组,合法下标范围是0N-1

C
int scores[5] = {90, 85, 78, 92, 88};

int first = scores[0];
int last = scores[4];

scores[2] = 100;

下标可以是整型表达式,这为循环遍历提供了便利:

C
int i = 3;
int val = scores[i];
💡 记住:长度为5的数组,最大下标是4,不是5。C语言对下标不做事前检查,越界访问不会报错,但后果严重。


3. 数组越界风险

C语言不检查数组下标是否越界。访问越界元素属于未定义行为,可能读取到垃圾数据,可能修改其他变量的值,甚至导致程序崩溃。

C
int arr[3] = {10, 20, 30};

printf("%d\n", arr[3]);
printf("%d\n", arr[-1]);

上面两行都访问了非法位置。编译器不会警告,但结果不可预测。

⚠️ 越界是C语言最常见的Bug来源之一。写循环时务必确认边界条件,使用<而非<=来对比长度。


4. 数组遍历

for循环逐个处理数组元素是最常见的操作:

C
int scores[5] = {90, 85, 78, 92, 88};
int i;

for (i = 0; i < 5; i++) {
    printf("scores[%d] = %d\n", i, scores[i]);
}

求所有元素之和:

C
int sum = 0;
for (i = 0; i < 5; i++) {
    sum += scores[i];
}
printf("总分: %d\n", sum);

找出最大值:

C
int max = scores[0];
for (i = 1; i < 5; i++) {
    if (scores[i] > max) {
        max = scores[i];
    }
}
printf("最高分: %d\n", max);

▶ 示例

计算一组成绩的平均分和不及格人数:

C
#include <stdio.h>

int main(void) {
    double scores[] = {78.5, 92.0, 55.5, 88.0, 43.0, 67.5, 90.0};
    int len = sizeof(scores) / sizeof(scores[0]);
    double sum = 0;
    int fail = 0;
    int i;

    for (i = 0; i < len; i++) {
        sum += scores[i];
        if (scores[i] < 60.0) {
            fail++;
        }
    }

    printf("平均分: %.1f\n", sum / len);
    printf("不及格人数: %d\n", fail);

    return 0;
}
▶ 试一试
TEXT 📖 仅展示
平均分: 73.5
不及格人数: 2

这里用sizeof(scores) / sizeof(scores[0])动态计算数组元素个数,这样修改数组内容时不用同步修改循环条件。


5. 数组作为函数参数

数组传递给函数时,不会复制整个数组,而是只传递首元素地址。这意味着函数内无法通过sizeof获知数组长度,必须额外传入长度参数。

C
void print_array(int arr[], int len) {
    int i;
    for (i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

参数int arr[]等价于int *arr,这是"退化"为指针的体现。函数内修改数组元素会影响原始数组,因为操作的是同一块内存。

C
void double_values(int arr[], int len) {
    int i;
    for (i = 0; i < len; i++) {
        arr[i] *= 2;
    }
}

int main(void) {
    int data[] = {1, 2, 3, 4, 5};
    double_values(data, 5);
    return 0;
}

调用后data的内容变为{2, 4, 6, 8, 10}


6. 二维数组

二维数组可以理解为"数组的数组",常用于表示矩阵或表格数据。

C
int matrix[3][4];

定义了一个3行4列的二维数组。初始化可以按行分组:

C
int matrix[3][4] = {
    {1,  2,  3,  4},
    {5,  6,  7,  8},
    {9, 10, 11, 12}
};

也可以平铺写,效果相同:

C
int matrix[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

访问元素使用两个下标:

C
int val = matrix[1][2];
matrix[0][0] = 100;

遍历二维数组需要双重循环:

C
int i, j;
for (i = 0; i < 3; i++) {
    for (j = 0; j < 4; j++) {
        printf("%4d", matrix[i][j]);
    }
    printf("\n");
}

初始化时可以省略行数,但不能省略列数:

C
int m[][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8}
};

编译器根据初始化数据推断出2行。

▶ 示例

计算3名学生的4门课平均成绩:

C
#include <stdio.h>

void student_avg(double grades[][4], int rows) {
    int i, j;
    for (i = 0; i < rows; i++) {
        double sum = 0;
        for (j = 0; j < 4; j++) {
            sum += grades[i][j];
        }
        printf("学生%d 平均分: %.1f\n", i + 1, sum / 4);
    }
}

int main(void) {
    double grades[3][4] = {
        {85.0, 90.0, 78.0, 92.0},
        {70.0, 65.0, 80.0, 75.0},
        {95.0, 88.0, 92.0, 97.0}
    };

    student_avg(grades, 3);

    return 0;
}
▶ 试一试
TEXT 📖 仅展示
学生1 平均分: 86.2
学生2 平均分: 72.5
学生3 平均分: 93.0
💡 二维数组作为函数参数时,必须指定列数(第二维大小),行数通过额外参数传入。

❓ 常见问题

Q 数组定义后不初始化,里面是什么值?
A 局部数组内容不确定(栈上的垃圾值);全局数组和静态数组自动初始化为0。
Q sizeof(arr)在函数内部为什么不能用来求元素个数?
A 数组传参后退化为指针,sizeof得到的是指针大小(4或8字节),而非数组总大小。
Q 数组下标可以为负数吗?
A 语法上可以编译,但访问的是数组起始地址之前的内存,属于越界访问,是未定义行为。
Q 二维数组在内存中是怎么存放的?
A 按行优先顺序连续存放,先存完第0行所有元素,再存第1行,以此类推。

📖 小节

📝 作业

  1. 编写程序,输入10个整数到数组中,然后逆序输出所有元素。
  2. 编写函数,接收一个整型数组和长度,返回数组中最大值的下标。
  3. 定义一个4×4的整型矩阵并初始化,计算并输出两条对角线元素之和。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏