C语言数组

在编程中,经常需要处理一组相同类型的数据,比如一个班50名学生的成绩、一个矩阵的元素等。如果为每个数据单独定义变量,不仅繁琐而且难以维护。 数组正是为了解决这一问题而设计的——它是一组具有相同数据类型的有序集合,通过统一的数组名和下标(索引)来访问各个元素。 本章将详细介绍C语言中数组的使用方法。

{{-- 1. 一维数组 --}}

📊 一维数组

一维数组是最简单的数组形式,可以看作是一个线性的数据序列。

🔹 数组的声明

类型 数组名[元素个数];

例如:int scores[10]; 定义一个包含10个整数的数组,索引从0到9。

注意: 数组长度必须是一个常量表达式(C99支持变长数组VLA,但一般推荐使用常量),且不能为0。

🔹 数组的初始化

可以在声明时初始化:

int arr1[5] = {1, 2, 3, 4, 5};      // 完全初始化
int arr2[5] = {1, 2, 3};             // 部分初始化,其余元素自动为0
int arr3[] = {1, 2, 3, 4, 5};        // 省略长度,编译器自动计算
int arr4[5] = {0};                   // 将所有元素初始化为0
int arr5[5] = {[2]=10, [4]=20};      // C99指定初始化器,将索引2设为10,索引4设为20

🔹 数组元素的访问与修改

通过下标(索引)访问数组元素,下标从0开始。语法:数组名[下标]

#include <stdio.h>

int main() {
    int scores[5] = {85, 92, 78, 90, 88};

    // 访问第三个元素(索引2)
    printf("第三个成绩: %d\n", scores[2]);   // 输出78

    // 修改第一个元素
    scores[0] = 95;
    printf("修改后第一个成绩: %d\n", scores[0]);   // 输出95

    return 0;
}

🔹 遍历数组

通常使用for循环遍历数组元素:

#include <stdio.h>

int main() {
    int scores[] = {85, 92, 78, 90, 88};
    int length = sizeof(scores) / sizeof(scores[0]);  // 计算数组元素个数

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

    return 0;
}
计算数组长度的技巧: sizeof(数组名) / sizeof(数组元素类型),此方法只能在数组定义的作用域内有效,当数组作为函数参数传递时会退化为指针,无法计算长度。

📝 综合示例:求数组最大值、最小值、平均值

#include <stdio.h>

int main() {
    int arr[] = {23, 45, 12, 67, 34, 89, 21, 54, 76, 33};
    int n = sizeof(arr) / sizeof(arr[0]);
    int max = arr[0], min = arr[0];
    int sum = 0;

    for (int i = 0; i < n; i++) {
        sum += arr[i];
        if (arr[i] > max) max = arr[i];
        if (arr[i] < min) min = arr[i];
    }

    double avg = (double)sum / n;
    printf("最大值: %d\n", max);
    printf("最小值: %d\n", min);
    printf("平均值: %.2f\n", avg);

    return 0;
}
{{-- 2. 二维数组 --}}

📊 二维数组

二维数组可以看作是一个表格或矩阵,由行和列组成。

🔹 声明与初始化

类型 数组名[行数][列数];

初始化方式:

// 方式1:按行初始化
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};

// 方式2:逐个元素初始化(按行优先存储)
int matrix2[2][3] = {1, 2, 3, 4, 5, 6};

// 方式3:部分初始化,未指定的元素为0
int matrix3[2][3] = {{1, 2}, {4}};   // 第一行第三列为0,第二行后两列为0

// 方式4:省略行数(必须指定列数)
int matrix4[][3] = {{1, 2, 3}, {4, 5, 6}};  // 编译器自动计算行数

🔹 访问与遍历

#include <stdio.h>

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

    // 遍历二维数组
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%3d ", matrix[i][j]);
        }
        printf("\n");
    }

    // 访问单个元素
    printf("matrix[1][2] = %d\n", matrix[1][2]);  // 输出7

    return 0;
}

📝 示例:矩阵乘法

#include <stdio.h>

int main() {
    int A[2][3] = {{1, 2, 3}, {4, 5, 6}};
    int B[3][2] = {{7, 8}, {9, 10}, {11, 12}};
    int C[2][2] = {0};  // 结果矩阵

    // 矩阵乘法:C[i][j] = sum(A[i][k] * B[k][j])
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            for (int k = 0; k < 3; k++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }

    // 输出结果
    printf("矩阵乘积结果:\n");
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            printf("%4d ", C[i][j]);
        }
        printf("\n");
    }

    return 0;
}
{{-- 3. 字符数组与字符串 --}}

🔤 字符数组与字符串

C语言中没有专门的字符串类型,字符串是通过字符数组来存储的,以空字符 '\0' 结尾。

🔹 声明与初始化

// 方式1:指定长度,并留出'\0'的位置
char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

// 方式2:使用字符串常量(自动添加'\0')
char str2[6] = "Hello";     // 等价于上面的写法
char str3[] = "Hello";      // 编译器自动计算长度(6)

// 方式3:只初始化部分,其余为0(即'\0')
char str4[10] = "Hello";    // 前5个字符是Hello,第6个是'\0',后面全是0
注意: 字符串长度(不包括'\0')必须小于数组长度,否则会溢出。

🔹 字符串的输入与输出

#include <stdio.h>
#include <string.h>

int main() {
    char name[50];

    printf("请输入姓名: ");
    fgets(name, sizeof(name), stdin);  // 安全读取一行,包含换行符

    // 移除换行符
    size_t len = strlen(name);
    if (len > 0 && name[len-1] == '\n') {
        name[len-1] = '\0';
    }

    printf("你好, %s\n", name);   // %s 会输出直到'\0'

    // 使用puts输出(自动换行)
    puts("欢迎学习C语言");

    return 0;
}

🔹 常用字符串处理函数(需包含 string.h)

函数功能示例
strlen(str)返回字符串长度(不含'\0')len = strlen("Hello"); → 5
strcpy(dest, src)将src复制到dest(不安全,可能溢出)strcpy(str1, "World");
strncpy(dest, src, n)最多复制n个字符,更安全strncpy(str1, "Hello", 5);
strcat(dest, src)将src追加到dest末尾strcat(greeting, "!");
strcmp(str1, str2)比较字符串,返回0表示相等if (strcmp(s1, s2) == 0)
#include <stdio.h>
#include <string.h>

int main() {
    char str1[20] = "Hello";
    char str2[] = "World";

    // 连接
    strcat(str1, " ");    // 注意确保空间足够
    strcat(str1, str2);
    printf("拼接结果: %s\n", str1);   // "Hello World"

    // 复制
    char str3[20];
    strcpy(str3, str1);
    printf("复制结果: %s\n", str3);

    // 比较
    if (strcmp(str1, str3) == 0) {
        printf("str1 和 str3 相等\n");
    }

    return 0;
}
{{-- 4. 数组作为函数参数 --}}

📦 数组作为函数参数

当数组作为函数参数时,实际上传递的是数组首元素的指针,因此在函数内部无法直接获取数组的长度,需要额外传递长度参数。

#include <stdio.h>

// 函数声明:接收一个整型数组和数组长度
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

void doubleElements(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        arr[i] *= 2;   // 修改会影响原数组
    }
}

int main() {
    int nums[] = {1, 2, 3, 4, 5};
    int len = sizeof(nums) / sizeof(nums[0]);

    printf("原数组: ");
    printArray(nums, len);

    doubleElements(nums, len);

    printf("翻倍后: ");
    printArray(nums, len);

    return 0;
}
注意: 数组作为参数时,形参的 int arr[] 实际上等同于 int *arr,所以对形参的修改会影响实参数组。
{{-- 5. 常见错误与注意事项 --}}

⚠️ 常见错误与注意事项

  • 数组越界: C语言不检查数组下标是否越界,访问超出范围的内存可能导致数据损坏或程序崩溃。务必确保下标在合法范围内。
  • 字符串缺少'\0': 如果字符数组没有以'\0'结尾,使用printf("%s")等函数会继续读取内存直到遇到'\0',造成不可预知的结果。
  • 使用未初始化的数组元素: 局部数组中的元素默认是垃圾值,使用前应初始化。
  • 对数组名取地址: 数组名本身是地址常量,不可修改。但&arrarr的值相同,但类型不同(指针与数组指针),需注意区别。
  • 函数中无法通过sizeof获取数组长度: 因为数组参数退化为指针,sizeof(arr)得到的是指针大小,而不是数组大小。
  • 变长数组(VLA)的局限性: C99支持变长数组,但并非所有编译器完全支持,且存在一些限制。

数组是C语言中最常用的数据结构之一,它为批量处理同类型数据提供了方便。一维数组、二维数组以及字符数组(字符串)是后续学习指针、动态内存分配等高级特性的基础。 下一章我们将学习函数,将程序分解为可重用的模块。

{{-- 章节导航按钮 --}}