文章

C 数组

C 数组

在 C 语言中,数组(Array)是一种用于存储固定数量、相同类型数据的集合。数组提供了一种连续的内存结构,让我们能以索引方式访问和操作元素。

数组的定义和初始化

  1. 定义数组

    1
    2
    3
    
     int arr[5];           // 定义一个整型数组,包含5个元素,初值不确定
     float prices[10];     // 包含10个 float 类型元素
     char name[20];        // 字符数组(可用于存储字符串)
    

    数组下标从 0 开始,即 arr[0] 是第一个元素,arr[4] 是最后一个元素(共5个)。

  2. 初始化数组

    1
    2
    3
    4
    
     int a[5] = {1, 2, 3, 4, 5};     // 完全初始化
     int b[5] = {1, 2};              // 部分初始化,其余自动填 0
     int c[] = {10, 20, 30};         // 自动推导长度为 3
     char s[] = "hello";             // 自动加上 '\0',长度为 6
    

数组的访问

通过索引访问(从 0 到 n-1):

1
2
a[0] = 10;
printf("%d\n", a[2]);

越界访问(如 a[5])会导致未定义行为。

数组的元素修改

1
2
3
4
5
6
int myNumbers[] = {25, 50, 75, 100};
myNumbers[0] = 33;

printf("%d", myNumbers[0]);

// 现在输出 33 而不是 25

数组和指针的关系

在大多数表达式中,数组名会退化为指向第一个元素的指针

1
2
3
int a[5] = {1,2,3,4,5};
int *p = a;         // 等同于 int *p = &a[0];
printf("%d\n", *(p + 2));   // 输出 3

数组作为函数参数

1
2
3
4
void printArr(int arr[], int len) {
    for (int i = 0; i < len; i++)
        printf("%d ", arr[i]);
}

在函数中,arr[] 实际上传递的是指针,不包含长度信息,需手动传入长度参数。

多维数组

  1. 定义多维数组

    1
    2
    3
    4
    
     int mat[2][3] = {
         {1, 2, 3},
         {4, 5, 6}
     };
    
  2. 访问多维数组

    1
    
     printf("%d\n", mat[1][2]);  // 输出 6
    

字符数组

  • 字符串本质上是以 '\0' 结尾的字符数组(char[])。
  • 字符串数组通常指包含多个字符串(即多个字符数组)的二维数组。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
char str1[6] = "hello";  // 包括结尾的 '\0',共6个字节
char str2[] = "hello";  // 自动推断长度为6个字节
char str3[10] = "hello";    // 包括字符"hello" + "\0" + 4个空位
char str4[] = {'h','e','l','l','o','\0'};  // 手动初始化

// 字符串指针数组
const char * colors[] = {
    "red", "green", "blue"
}

// 每一个元素都是char[]
// 优势:总内存更节省,可以指向不同长度的字符串
// 字符串的内容不能修改

// 二维数组
char fruits[3][10] = {
    "apple",    // 实际为 {'a','p','p','l','e','\0', ...}
    "banana",
    "cherry"
};

// fruits[i] 是第 i 个字符串
// 每行最多能存放 9 个可见字符 + \0(共 10 字节)
  • 字符数组的访问方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
char fruits[3][10] = {
    "apple",
    "banana",
    "cherry"
};

printf("%s\n", fruits[1]);      // 输出 "banana"
printf("%c\n", fruits[0][2]);   // 输出 'p'
printf("%s\n", colors[2]);      // 输出 "blue"

// 字符串指针数组
const char *pets[] = {"dog", "cat", "bird"};

printf("%s\n", pets[2]);         // 输出 "bird"
char c = pets[0][1];             // 'o'

二维数组和指针数组的对比

特性char arr[N][M]char *arr[]
内存结构一块连续内存(固定大小)每项是指针,指向常量字符串
占用空间更大更小(更节省)
可否修改内容✅ 可以(写进数组)❌ 常量字符串,不可以修改内容
是否支持变长字符串❌ 长度固定✅ 每个字符串可不同长度

malloc申请空间

malloc() 可以实现 动态内存分配,用于在运行时按需创建数组(尤其是长度不确定时)。

用途:

  • 数组长度在运行时才知道
  • 数组需要跨函数使用
  • 需要在堆上分配内存(避免栈溢出)
  • 可与 realloc 搭配动态扩容

一维数组动态分配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>

int main() {
    int n;
    scanf("%d", &n);

    int *arr = (int *)malloc(n * sizeof(int));  // 分配 n 个 int 的空间

    if (arr == NULL) {
        printf("Memory allocation failed.\n");
        return 1;
    }

    for (int i = 0; i < n; i++)
        arr[i] = i * i;

    for (int i = 0; i < n; i++)
        printf("%d ", arr[i]);

    free(arr);  // 释放内存
    return 0;
}

二维数组动态分配(常用两种方式)

  • 方法 1:分配“指针数组 + 每行”
1
2
3
4
5
6
7
8
9
10
11
12

int **matrix = (int **)malloc(rows * sizeof(int *));
for (int i = 0; i < rows; i++) {
    matrix[i] = (int *)malloc(cols * sizeof(int));
}

for (int i = 0; i < rows; i++) {
    free(matrix[i]);
}
free(matrix);

  • 方法 2:一次性分配整个二维块(更高效)
1
2
3
4
5
6
7
8
9
10
int *data = (int *)malloc(rows * cols * sizeof(int));
int **matrix = (int **)malloc(rows * sizeof(int *));
for (int i = 0; i < rows; i++) {
    matrix[i] = data + i * cols;
}

free(matrix);
free(data);

calloc() 和 realloc()

calloc():自动初始化为 0

1
int *a = (int *)calloc(n, sizeof(int));

realloc():动态扩容数组

1
2
int *a = (int *)malloc(5 * sizeof(int));
a = (int *)realloc(a, 10 * sizeof(int));  // 扩容为10个元素

计算数组的长度

1
2
int a[5] = {1,2,3,4,5};
int len = sizeof(a) / sizeof(a[0]);  // 计算数组长度(元素个数)

注意事项

问题/陷阱说明
数组越界会造成未定义行为,可能崩溃或错误数据
使用未初始化的数组元素会导致不可预测的值
数组大小必须是常量(C89)C89 中不支持变长数组(VLA)
数组与指针混淆虽然语法上接近,但数组和指针在内存上是不同的
本文由作者按照 CC BY 4.0 进行授权