前置知识: C

数组详解

00:00
7 min Intermediate

一维、多维数组、字符数组、数组与指针的关系及内存布局。

1. 数组的概念与特性

1.1 什么是数组

  • 数组是一组相同类型的元素的集合,存储在连续的内存地址中。
  • 特点
  • 所有元素类型相同
  • 元素在内存中连续存储
  • 通过下标访问元素(下标从 0 开始)
  • 数组大小在声明时确定(除变长数组外)

2. 一维数组

2.1 定义与声明

  • 格式type array_name[size];
  • 示例
 int numbers[5]; // 整型数组,大小为 5
 float prices[10]; // 浮点型数组,大小为 10
 char letters[26]; // 字符型数组,大小为 26

2.2 初始化

2.2.1 完全初始化

 int arr[5] = {1, 2, 3, 4, 5}; // 初始化所有元素

2.2.2 部分初始化

 int arr[5] = {1, 2, 3}; // 前 3 个元素初始化,其余为 0

2.2.3 自动推断大小

 int arr[] = {10, 20, 30, 40, 50}; // 数组大小自动推断为 5

2.2.4 全部初始化为 0

 int arr[10] = {0}; // 所有元素初始化为 0

2.3 访问元素

  • 格式array_name[index]
  • 示例
 int arr[5] = {10, 20, 30, 40, 50};
 printf("arr[0] = %d\n", arr[0]); // 输出 10
 printf("arr[2] = %d\n", arr[2]); // 输出 30

2.4 遍历数组

 int arr[5] = {1, 2, 3, 4, 5};
 // 使用 for 循环遍历
 for (int i = 0; i < 5; i++) {
  printf("arr[%d] = %d\n", i, arr[i]);
 }

2.5 数组大小计算

  • 使用 sizeof 运算符计算数组大小:
 int arr[] = {1, 2, 3, 4, 5};
 int size = sizeof(arr) / sizeof(arr[0]);
 printf("数组大小: %d\n", size); // 输出 5

2.6 数组越界

  • 注意:C 语言不检查数组下标越界,越界访问可能导致:
  • 访问到无效内存,导致程序崩溃
  • 修改其他变量的值,导致数据损坏
  • 安全漏洞(如缓冲区溢出)
 int arr[5] = {1, 2, 3, 4, 5};
 arr[10] = 100; // 越界访问,危险!

3. 多维数组

3.1 二维数组

  • 概念:可以看作是由多个一维数组组成的数组。
  • 定义type array_name[rows][columns];
  • 内存布局:行优先存储(按行顺序存储元素)

3.1.1 初始化

 // 完整初始化
 int matrix[2][3] = {
  {1, 2, 3},
  {4, 5, 6}
 }
 // 部分初始化
 int matrix[2][3] = {{1, 2}, {4}}; // 其余元素为 0
 // 自动推断行数
 int matrix[][3] = {{1, 2, 3}, {4, 5, 6}}; // 行数自动推断为 2

3.1.2 访问元素

 int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
 printf("matrix[0][0] = %d\n", matrix[0][0]); // 输出 1
 printf("matrix[1][2] = %d\n", matrix[1][2]); // 输出 6

3.1.3 遍历二维数组

 int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
 for (int i = 0; i < 2; i++) { // 遍历行
  for (int j = 0; j < 3; j++) { // 遍历列
  printf("%d ", matrix[i][j]);
  }
  printf("\n");
 }

3.2 三维及以上数组

  • 定义type array_name[depth][rows][columns];
  • 示例
 int cube[2][2][2] = {
 {{1, 2}, {3, 4}},
 {{5, 6}, {7, 8}}
 };

3.3 多维数组作为函数参数

  • 传递方式:需要指定除第一维外的所有维度大小。
  • 示例
 void print_matrix(int matrix[][3], int rows) {
 for (int i = 0; i < rows; i++) {
 for (int j = 0; j < 3; j++) {
 printf("%d ", matrix[i][j]);
 }
 printf("\n");
 }
 }

4. 字符数组与字符串

4.1 字符数组

  • 定义char array_name[size];
  • 初始化
 char chars[5] = {'H', 'e', 'l', 'l', 'o'}; // 普通字符数组
 char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // 字符串(以 '\0' 结尾)

4.2 字符串

  • 概念:以空字符 '\0' 结尾的字符数组。
  • 初始化
 char str[] = "Hello"; // 自动包含 '\0',大小为 6
 char str[10] = "Hello"; // 剩余空间填充 '\0'

4.3 字符串操作函数

  • 包含头文件#include <string.h>
    函数功能示例
    strlen()计算字符串长度(不包括 ‘\0’)int len = strlen(str);
    strcpy()复制字符串strcpy(dest, src);
    strcat()连接字符串strcat(dest, src);
    strcmp()比较字符串int result = strcmp(str1, str2);
    strncpy()复制指定长度的字符串strncpy(dest, src, n);
    strncat()连接指定长度的字符串strncat(dest, src, n);
    strncmp()比较指定长度的字符串int result = strncmp(str1, str2, n);

4.4 字符串输入输出

 char str[100];
 // 输入字符串(遇到空格停止)
 scanf("%s", str);
 // 输入一行字符串(包括空格)
 fgets(str, sizeof(str), stdin);
 // 输出字符串
 printf("%s\n", str);

4.5 常见问题与注意事项

  • 缓冲区溢出:使用 fgets() 替代 gets() 以避免缓冲区溢出
  • 字符串长度:使用 strlen() 时要注意字符串必须以 '\0' 结尾
  • 内存分配:确保目标字符串有足够的空间存储源字符串

5. 数组与指针的关系

5.1 数组名的特性

  • 数组名是数组首元素的地址,是一个常量指针(不能修改)。
  • 等价关系arr 等同于 &arr[0]
 int arr[5] = {1, 2, 3, 4, 5};
 printf("arr = %p\n", arr); // 数组首元素地址
 printf("&arr[0] = %p\n", &arr[0]); // 数组首元素地址
 printf("arr[0] = %d\n", *arr); // 数组首元素值

5.2 指针算术与数组访问

  • 指针算术:指针可以进行加减运算,单位是所指向类型的大小。
  • 数组访问arr[i] 等同于 *(arr + i)
 int arr[5] = {1, 2, 3, 4, 5};
 int *p = arr; // 指向数组首元素
 printf("*p = %d\n", *p); // 输出 1
 printf("*(p + 1) = %d\n", *(p + 1)); // 输出 2
 printf("p[2] = %d\n", p[2]); // 输出 3

5.3 数组作为函数参数

  • 数组退化:数组作为函数参数时,会退化为指向首元素的指针。
  • 注意:函数内部无法通过 sizeof 获取数组的总大小。
 // 函数声明
 void print_array(int *arr, int size);
 // 函数定义
 void print_array(int *arr, int size) {
  for (int i = 0; i < size; i++) {
  printf("%d ", arr[i]);
  }
  printf("\n");
 }
 // 调用
 int main() {
  int numbers[] = {1, 2, 3, 4, 5};
  int size = sizeof(numbers) / sizeof(numbers[0]);
  print_array(numbers, size);
  return 0;
 }

5.4 指针数组

  • 定义:存储指针的数组。
  • 示例
 int *ptr_array[5]; // 存储 5 个 int 指针的数组
 // 字符串数组(实际上是字符指针数组)
 char *str_array[] = {
 "Hello",
 "World",
 "C Language"
 };

5.5 数组指针

  • 定义:指向数组的指针。
  • 格式type (*pointer_name)[size];
  • 示例
 int arr[5] = {1, 2, 3, 4, 5};
 int (*p)[5] = &arr; // 指向整个数组的指针
 printf("*(*p) = %d\n", *(*p)); // 输出 1
 printf("*(*p + 1) = %d\n", *(*p + 1)); // 输出 2

6. 变长数组 (VLA - Variable Length Arrays)

6.1 概念

  • 变长数组:C99 引入,允许在运行时确定数组大小。
  • 限制
  • 只能在函数内部声明(局部变量)
  • 不能初始化
  • 不支持全局变长数组

6.2 示例

 void func(int n) {
  int arr[n]; // 数组大小由参数 n 决定
  // 初始化数组
  for (int i = 0; i < n; i++) {
  arr[i] = i + 1;
  }
  // 遍历数组
  for (int i = 0; i < n; i++) {
  printf("%d ", arr[i]);
  }
  printf("\n");
 }
 int main() {
  func(5); // 传递数组大小
  return 0;
 }

7. 动态数组

7.1 概念

  • 动态数组:使用动态内存分配函数(如 malloccalloc)创建的数组。
  • 优点:可以在运行时动态调整大小。

7.2 示例

 #include <stdio.h>
 #include <stdlib.h>
 int main() {
  int size;
  printf("Enter array size: ");
  scanf("%d", &size);
  // 分配内存
  int *arr = (int *)malloc(size * sizeof(int));
  if (arr == NULL) {
  printf("Memory allocation failed!\n");
  return 1;
  }
  // 初始化数组
  for (int i = 0; i < size; i++) {
  arr[i] = i + 1;
  }
  // 遍历数组
  printf("Array elements: ");
  for (int i = 0; i < size; i++) {
  printf("%d ", arr[i]);
  }
  printf("\n");
  // 释放内存
  free(arr);
  return 0;
 }

7.3 动态调整数组大小

 // 重新分配内存
 int *new_arr = (int *)realloc(arr, new_size * sizeof(int));
 if (new_arr == NULL) {
  printf("Memory reallocation failed!\n");
  free(arr);
  return 1;
 }
 arr = new_arr;

8. 数组的高级应用

8.1 数组排序

 // 冒泡排序
 void bubble_sort(int arr[], int size) {
  for (int i = 0; i < size - 1; i++) {
  for (int j = 0; j < size - i - 1; j++) {
  if (arr[j] > arr[j + 1]) {
  // 交换元素
  int temp = arr[j];
  arr[j] = arr[j + 1];
  arr[j + 1] = temp;
  }
  }
  }
 }
 // 选择排序
 void selection_sort(int arr[], int size) {
  for (int i = 0; i < size - 1; i++) {
  int min_idx = i;
  for (int j = i + 1; j < size; j++) {
  if (arr[j] < arr[min_idx]) {
  min_idx = j;
  }
  }
  // 交换元素
  int temp = arr[i];
  arr[i] = arr[min_idx];
  arr[min_idx] = temp;
  }
 }

8.2 数组查找

 // 线性查找
 int linear_search(int arr[], int size, int target) {
  for (int i = 0; i < size; i++) {
  if (arr[i] == target) {
  return i; // 返回索引
  }
  }
  return -1; // 未找到
 }
 // 二分查找(要求数组已排序)
 int binary_search(int arr[], int low, int high, int target) {
  if (low > high) {
  return -1; // 未找到
  }
  int mid = low + (high - low) / 2;
  if (arr[mid] == target) {
  return mid; // 找到
  } else if (arr[mid] > target) {
  return binary_search(arr, low, mid - 1, target);
  } else {
  return binary_search(arr, mid + 1, high, target);
  }
 }

8.3 二维数组的应用

 // 矩阵加法
 void matrix_add(int a[][3], int b[][3], int result[][3], int rows) {
  for (int i = 0; i < rows; i++) {
  for (int j = 0; j < 3; j++) {
  result[i][j] = a[i][j] + b[i][j];
  }
  }
 }
 // 矩阵转置
 void matrix_transpose(int matrix[][3], int transposed[][2], int rows, int cols) {
  for (int i = 0; i < rows; i++) {
  for (int j = 0; j < cols; j++) {
  transposed[j][i] = matrix[i][j];
  }
  }
 }

9. 数组的最佳实践

9.1 命名规范

  • 数组名应清晰描述其内容,使用 snake_case 命名风格
  • 示例:student_scores, monthly_sales

9.2 内存管理

  • 对于大型数组,考虑使用动态内存分配
  • 动态分配的内存使用完毕后必须释放,避免内存泄漏
  • 避免使用过大的局部数组,可能导致栈溢出

9.3 性能优化

  • 缓存友好:按内存顺序访问数组(行优先)
  • 减少计算:预先计算数组大小,避免重复计算
  • 避免越界:使用断言或边界检查确保数组访问安全

9.4 代码风格

  • 缩进:使用一致的缩进风格
  • 注释:为复杂的数组操作添加注释
  • :保持代码式的一致性

10. 数组示例:完整应用

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 // 函数声明
 void print_array(int arr[], int size);
 void sort_array(int arr[], int size);
 int find_max(int arr[], int size);
 int find_min(int arr[], int size);
 double calculate_average(int arr[], int size);
 int main() {
  int size;
  printf("Enter array size: ");
  scanf("%d", &size);
  // 动态分配内存
  int *arr = (int *)malloc(size * sizeof(int));
  if (arr == NULL) {
  printf("Memory allocation failed!\n");
  return 1;
  }
  // 输入数组元素
  printf("Enter %d elements: ", size);
  for (int i = 0; i < size; i++) {
  scanf("%d", &arr[i]);
  }
  // 打印原始数组
  printf("Original array: ");
  print_array(arr, size);
  // 排序数组
  sort_array(arr, size);
  printf("Sorted array: ");
  print_array(arr, size);
  // 计算统计信息
  int max = find_max(arr, size);
  int min = find_min(arr, size);
  double average = calculate_average(arr, size);
  printf("Max: %d\n", max);
  printf("Min: %d\n", min);
  printf("Average: %.2f\n", average);
  // 释放内存
  free(arr);
  return 0;
 }
 // 打印数组
 void print_array(int arr[], int size) {
  for (int i = 0; i < size; i++) {
  printf("%d ", arr[i]);
  }
  printf("\n");
 }
 // 冒泡排序
 void sort_array(int arr[], int size) {
  for (int i = 0; i < size - 1; i++) {
  for (int j = 0; j < size - i - 1; j++) {
  if (arr[j] > arr[j + 1]) {
  int temp = arr[j];
  arr[j] = arr[j + 1];
  arr[j + 1] = temp;
  }
  }
  }
 }
 // 查找最大值
 int find_max(int arr[], int size) {
  int max = arr[0];
  for (int i = 1; i < size; i++) {
  if (arr[i] > max) {
  max = arr[i];
  }
  }
  return max;
 }
 // 查找最小值
 int find_min(int arr[], int size) {
  int min = arr[0];
  for (int i = 1; i < size; i++) {
  if (arr[i] < min) {
  min = arr[i];
  }
  }
  return min;
 }
 // 计算平均值
 double calculate_average(int arr[], int size) {
  int sum = 0;
  for (int i = 0; i < size; i++) {
  sum += arr[i];
  }
  return (double)sum / size;
 }

11. 常见错误与调试

11.1 常见错误

  • 数组越界:访问超出数组范围的元素
  • 内存泄漏动态分配的内存未释放
  • 空指针:使用未初始化指针访问数组
  • 字符串没有结束符:导致 strlen()函数出错

11.2 调试技巧

  • 打印数组内容检查数组元素是否正确
  • 使用调试器:如 GDB 执行,查看数组
  • 边界检查:在循环中添加边界检查
  • 内存检查:使用工具如 Valgrind 检查内存泄漏

更新日志 (Changelog)

  • 2026-04-05: 拆分并细化数组知识。
  • 2026-04-05: 详细扩写内容增加数组概念特性、一维数组详解、多维数组详解、字符数组字符串操作、数组指针关系详解、变长数组动态数组数组高级应用、最佳实践完整应用示例。

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式