C语言基础-五、数组

目录

1、数组的定义

[1.1 基本概念](#1.1 基本概念)

[1.2 数组的本质](#1.2 数组的本质)

[1.3 数组特点](#1.3 数组特点)

[1.4 地址计算公式](#1.4 地址计算公式)

2、数组的初始化

[2.1 初始化方式](#2.1 初始化方式)

[2.2 默认值规则](#2.2 默认值规则)

[2.3 数组越界访问(⚠️ 重点)](#2.3 数组越界访问(⚠️ 重点))

[2.4 栈空间限制](#2.4 栈空间限制)

3、元素访问

[3.1 索引规则](#3.1 索引规则)

[3.2 访问方式](#3.2 访问方式)

4、数组遍历

[4.1 基本遍历](#4.1 基本遍历)

[4.2 数组长度计算](#4.2 数组长度计算)

5、内存中的变量与数组

[5.1 内存地址基础](#5.1 内存地址基础)

[5.2 操作系统与地址空间](#5.2 操作系统与地址空间)

[5.3 地址打印示例](#5.3 地址打印示例)

[5.4 索引的本质](#5.4 索引的本质)

6、一维数组常见问题

[6.1 数组作为函数参数](#6.1 数组作为函数参数)

[6.2 数组退化原理](#6.2 数组退化原理)

[6.3 索引越界防护](#6.3 索引越界防护)

7、数组算法题

[7.1 求最值](#7.1 求最值)

[7.2 数组求和与平均](#7.2 数组求和与平均)

[7.3 生成不重复随机数](#7.3 生成不重复随机数)

[7.4 反转数组](#7.4 反转数组)

[7.5 打乱数组(洗牌算法)](#7.5 打乱数组(洗牌算法))

[7.6 查找算法](#7.6 查找算法)

(1)顺序查找

(2)二分查找(折半查找)

(3)查找算法对比

[7.7 排序算法](#7.7 排序算法)

(1)冒泡排序

(2)选择排序

(3)排序算法对比

8、二维数组

[8.1 基本概念](#8.1 基本概念)

[8.2 定义与初始化](#8.2 定义与初始化)

[8.3 内存布局](#8.3 内存布局)

[8.4 遍历二维数组](#8.4 遍历二维数组)

[8.5 二维数组作为函数参数](#8.5 二维数组作为函数参数)

[8.6 不规则二维数组(指针数组)](#8.6 不规则二维数组(指针数组))

9、动态数组

[9.1 栈数组 vs 堆数组](#9.1 栈数组 vs 堆数组)

[9.2 动态数组基本用法](#9.2 动态数组基本用法)

[9.3 动态数组函数返回](#9.3 动态数组函数返回)

[9.4 动态数组扩容](#9.4 动态数组扩容)

[9.5 动态二维数组](#9.5 动态二维数组)

10、常见错误与避坑指南

11、综合练习

[11.1 学生成绩管理系统](#11.1 学生成绩管理系统)

数组 :程序中用于存储相同数据类型 的多个值的连续内存空间

核心价值:批量处理数据、提高代码效率、便于数据管理


1、数组的定义

1.1 基本概念

cpp 复制代码
// 语法格式
数据类型 数组名[长度];

// 示例
int arr[5];        // 存储5个整数
float scores[10];  // 存储10个浮点数
char name[20];     // 存储20个字符

1.2 数组的本质

cpp 复制代码
[]运算符的本质:先计算地址,再访问元素

arr[i] 的原理:
1. 获取数组首地址 &arr[0]
2. 计算偏移量:i * sizeof(元素类型)
3. 目标地址 = 首地址 + 偏移量
4. 访问该地址处的数据

公式:&arr[i] = &arr[0] + i * sizeof(元素类型)

⚠️ 重要:数组长度在编译时确定,运行时是"无用信息",编译器只记录首地址和元素类型

1.3 数组特点

特点 说明
连续内存 数组元素在内存中连续存储
类型相同 所有元素必须是同一数据类型
有序性 元素按索引顺序排列
首地址 数组名 = &arr0(数组首地址)
长度固定 定义后长度不可改变
随机访问 可通过索引直接访问任意元素

1.4 地址计算公式

cpp 复制代码
// 数组第n个元素的地址
&arr[n] = 数组首地址 + n * sizeof(元素类型)

// 示例:int arr[5],首地址为0x1000
&arr[0] = 0x1000 + 0 * 4 = 0x1000
&arr[1] = 0x1000 + 1 * 4 = 0x1004
&arr[2] = 0x1000 + 2 * 4 = 0x1008
&arr[3] = 0x1000 + 3 * 4 = 0x100C
&arr[4] = 0x1000 + 4 * 4 = 0x1010

2、数组的初始化

2.1 初始化方式

cpp 复制代码
// 方式1:完全初始化
int arr1[5] = {1, 2, 3, 4, 5};

// 方式2:部分初始化(剩余元素自动补0)
int arr2[5] = {1, 2, 3};  // arr2[3]=0, arr2[4]=0

// 方式3:省略长度(由初始化元素个数决定)
int arr3[] = {1, 2, 3, 4, 5};  // 长度为5

// 方式4:全部置0
int arr4[5] = {0};      // 所有元素为0
int arr5[5] = {};       // 所有元素为0(C99标准)

// 方式5:指定索引初始化(C99)
int arr6[5] = {[2] = 10, [4] = 20};  // arr6[2]=10, arr6[4]=20, 其他为0

2.2 默认值规则

数据类型 默认值
整数类型 0
浮点类型 0.0
字符类型 '\0'
指针类型 NULL

2.3 数组越界访问(⚠️ 重点)

cpp 复制代码
//错误示例:数组越界
#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int a = 20;  // 在栈上,可能紧邻arr分配
    
    // 越界访问:arr的有效索引是0-4
    for (int i = 0; i < 13; i++) {  //循环13次,越界!
        arr[i] = i + 1;
    }
    
    printf("a = %d\n", a);  // 输出可能不是20!
    return 0;
}

//正确示例:使用sizeof计算长度
#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int a = 20;
    
    // 计算数组元素个数
    int arr_size = sizeof(arr) / sizeof(arr[0]);  // 5
    
    for (int i = 0; i < arr_size; i++) {  //循环5次
        arr[i] = i + 1;
    }
    
    printf("a = %d\n", a);  // 输出20
    return 0;
}

2.4 栈空间限制

cpp 复制代码
// 栈空间有限(通常1-8MB),大数组会导致栈溢出
#include <stdio.h>

int main() {
    // 可能导致栈溢出
    // int bigArr[1000000];  // 约4MB
    
    // 大数组应使用动态分配(堆空间)
    int *bigArr = (int*)malloc(1000000 * sizeof(int));
    if (bigArr != NULL) {
        // 使用数组...
        free(bigArr);
    }
    
    return 0;
}

💡 建议:超过1MB的数组建议使用动态分配(malloc)


3、元素访问

3.1 索引规则

规则 说明
从0开始 第一个元素索引为0
连续递增 索引依次+1,不间断
有效范围 0 到 长度-1

3.2 访问方式

cpp 复制代码
int arr[5] = {10, 20, 30, 40, 50};

// 获取元素
int value = arr[2];      // value = 30

// 修改元素
arr[2] = 300;            // arr[2]变为300

// 使用指针访问(等价)
int value2 = *(arr + 2); // value2 = 300
arr[2] = 400;            // 等价于 *(arr + 2) = 400

4、数组遍历

4.1 基本遍历

cpp 复制代码
int arr[] = {1, 2, 3, 4, 5};
int len = sizeof(arr) / sizeof(arr[0]);

// 正向遍历
for (int i = 0; i < len; i++) {
    printf("%d ", arr[i]);
}

// 反向遍历
for (int i = len - 1; i >= 0; i--) {
    printf("%d ", arr[i]);
}

4.2 数组长度计算

cpp 复制代码
// 公式:元素个数 = 总字节数 / 单个元素字节数
int len = sizeof(arr) / sizeof(arr[0]);
// 或
int len = sizeof(arr) / sizeof(int);

⚠️ 注意 :此方法仅适用于当前作用域内的数组,数组作为函数参数时会退化为指针,sizeof结果为指针大小(8字节)


5、内存中的变量与数组

5.1 内存地址基础

概念 说明
内存 程序运行时临时存储数据的空间
内存地址 内存格子的编号,一个格子=1字节
地址表示 十六进制(如0x7fff5fbff8ac)
地址查询 &变量名

5.2 操作系统与地址空间

系统类型 地址位数 最大支持内存
32位 32位二进制 4GB
64位 64位二进制 16EB(理论)

5.3 地址打印示例

cpp 复制代码
#include <stdio.h>

int main() {
    int a = 10;
    int arr[3] = {1, 2, 3};
    
    // 打印变量地址
    printf("a的地址: %p\n", (void*)&a);
    
    // 打印数组地址
    printf("arr的地址: %p\n", (void*)arr);
    printf("arr[0]的地址: %p\n", (void*)&arr[0]);
    printf("arr[1]的地址: %p\n", (void*)&arr[1]);
    printf("arr[2]的地址: %p\n", (void*)&arr[2]);
    
    // 验证地址连续性
    printf("地址间隔: %ld 字节\n", (char*)&arr[1] - (char*)&arr[0]);
    
    return 0;
}

5.4 索引的本质

cpp 复制代码
索引 = 偏移量(从首地址开始偏移的单位数)

arr[0] → 首地址 + 0 * sizeof(元素类型)
arr[1] → 首地址 + 1 * sizeof(元素类型)
arr[i] → 首地址 + i * sizeof(元素类型)

这就是为什么索引从0开始!

6、一维数组常见问题

6.1 数组作为函数参数

cpp 复制代码
#include <stdio.h>

// 错误:无法获取数组长度
void printArrWrong(int arr[]) {
    // sizeof(arr) = 8(指针大小),不是数组大小!
    int len = sizeof(arr) / sizeof(arr[0]);  // 错误!
    printf("长度: %d\n", len);
}

// 正确:额外传递长度参数
void printArr(int arr[], int len) {
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int len = sizeof(arr) / sizeof(arr[0]);
    
    printArr(arr, len);  // 正确调用
    
    return 0;
}

6.2 数组退化原理

cpp 复制代码
// 函数调用时的实际情况
func(arr);      // 等价于 func(&arr[0]);

// 本质:
// 1. 运行时:传递的是数组首地址
// 2. 编译时:数组类型"退化"为指针类型
// 3. 结果:长度信息丢失,需额外传递

// 三种等价的函数声明
void func1(int arr[]);
void func2(int *arr);
void func3(int arr[5]);  // 5被忽略

6.3 索引越界防护

检查项 正确值
最小索引 0
最大索引 长度 - 1
有效范围 [0, 长度)
cpp 复制代码
// 安全访问示例
int arr[5] = {1, 2, 3, 4, 5};
int index = 3;

if (index >= 0 && index < 5) {
    printf("%d\n", arr[index]);
} else {
    printf("索引越界!\n");
}

7、数组算法题

7.1 求最值

cpp 复制代码
#include <stdio.h>

int main() {
    int arr[] = {1, 4, 6, 8, 12, 164, 76, 34};
    int len = sizeof(arr) / sizeof(arr[0]);
    
    // 求最大值
    int max = arr[0];
    int maxIndex = 0;
    for (int i = 1; i < len; i++) {
        if (arr[i] > max) {
            max = arr[i];
            maxIndex = i;
        }
    }
    printf("最大值: %d (索引: %d)\n", max, maxIndex);
    
    // 求最小值
    int min = arr[0];
    int minIndex = 0;
    for (int i = 1; i < len; i++) {
        if (arr[i] < min) {
            min = arr[i];
            minIndex = i;
        }
    }
    printf("最小值: %d (索引: %d)\n", min, minIndex);
    
    return 0;
}

7.2 数组求和与平均

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
    int arr[10] = {0};
    int len = sizeof(arr) / sizeof(arr[0]);
    
    // 设置随机数种子
    srand(time(NULL));
    
    // 生成10个1-100的随机数
    printf("生成的随机数: ");
    for (int i = 0; i < len; i++) {
        arr[i] = rand() % 100 + 1;
        printf("%d ", arr[i]);
    }
    printf("\n\n");
    
    // 求和
    int sum = 0;
    for (int i = 0; i < len; i++) {
        sum += arr[i];
    }
    printf("总和: %d\n", sum);
    
    // 求平均数
    double avg = (double)sum / len;
    printf("平均数: %.2f\n\n", avg);
    
    // 统计比平均数小的元素个数
    int count = 0;
    for (int i = 0; i < len; i++) {
        if (arr[i] < avg) {
            count++;
        }
    }
    printf("比平均数小的元素个数: %d\n", count);
    
    return 0;
}

7.3 生成不重复随机数

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>

// 检查数字是否在数组中已存在
bool contains(int arr[], int len, int num) {
    for (int i = 0; i < len; i++) {
        if (arr[i] == num) {
            return true;
        }
    }
    return false;
}

int main() {
    int arr[10] = {0};
    int len = sizeof(arr) / sizeof(arr[0]);
    
    // 设置随机数种子
    srand(time(NULL));
    
    // 生成10个不重复的1-100随机数
    printf("生成的不重复随机数: ");
    for (int i = 0; i < len; ) {  // 注意:i只在成功添加时递增
        int num = rand() % 100 + 1;
        if (!contains(arr, i, num)) {  // 检查前i个元素
            arr[i] = num;
            i++;
        }
    }
    
    // 输出结果
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}

7.4 反转数组

cpp 复制代码
#include <stdio.h>

// 打印数组
void printArr(int arr[], int len) {
    printf("[");
    for (int i = 0; i < len; i++) {
        printf("%d", arr[i]);
        if (i < len - 1) printf(", ");
    }
    printf("]\n");
}

int main() {
    int arr[5];
    int len = sizeof(arr) / sizeof(arr[0]);
    
    // 键盘录入
    printf("请输入5个整数:\n");
    for (int i = 0; i < len; i++) {
        printf("第%d个: ", i + 1);
        scanf("%d", &arr[i]);
    }
    
    printf("反转前: ");
    printArr(arr, len);
    
    // 反转数组(双指针法)
    int left = 0;
    int right = len - 1;
    while (left < right) {
        int temp = arr[left];
        arr[left] = arr[right];
        arr[right] = temp;
        left++;
        right--;
    }
    
    printf("反转后: ");
    printArr(arr, len);
    
    return 0;
}

7.5 打乱数组(洗牌算法)

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// Fisher-Yates 洗牌算法(推荐)
void shuffle(int arr[], int len) {
    srand(time(NULL));
    
    for (int i = len - 1; i > 0; i--) {
        // 生成0到i之间的随机索引
        int j = rand() % (i + 1);
        
        // 交换arr[i]和arr[j]
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int len = sizeof(arr) / sizeof(arr[0]);
    
    printf("打乱前: ");
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    shuffle(arr, len);
    
    printf("打乱后: ");
    for (int i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    return 0;
}

7.6 查找算法

(1)顺序查找
cpp 复制代码
#include <stdio.h>

// 顺序查找:返回目标值的索引,未找到返回-1
int sequentialSearch(int arr[], int len, int target) {
    for (int i = 0; i < len; i++) {
        if (arr[i] == target) {
            return i;
        }
    }
    return -1;
}

int main() {
    int arr[] = {11, 22, 55, 77, 44};
    int len = sizeof(arr) / sizeof(arr[0]);
    int target = 55;
    
    int index = sequentialSearch(arr, len, target);
    
    if (index != -1) {
        printf("找到 %d,索引: %d\n", target, index);
    } else {
        printf("未找到 %d\n", target);
    }
    
    return 0;
}
(2)二分查找(折半查找)
cpp 复制代码
#include <stdio.h>

// 二分查找:数组必须有序!
int binarySearch(int arr[], int len, int target) {
    int left = 0;
    int right = len - 1;
    
    while (left <= right) {
        // 防止溢出的中间值计算
        int mid = left + (right - left) / 2;
        
        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] > target) {
            right = mid - 1;  // 在左半部分查找
        } else {
            left = mid + 1;   // 在右半部分查找
        }
    }
    
    return -1;  // 未找到
}

int main() {
    // 注意:数组必须有序!
    int arr[] = {11, 22, 44, 55, 77};  // 已排序
    int len = sizeof(arr) / sizeof(arr[0]);
    int target = 55;
    
    int index = binarySearch(arr, len, target);
    
    if (index != -1) {
        printf("找到 %d,索引: %d\n", target, index);
    } else {
        printf("未找到 %d\n", target);
    }
    
    return 0;
}
(3)查找算法对比
算法 时间复杂度 空间复杂度 前提条件
顺序查找 O(n) O(1)
二分查找 O(log n) O(1) 数组有序
插值查找 O(log log n) O(1) 有序且均匀分布
分块查找 O(√n) O(1) 块间有序

7.7 排序算法

(1)冒泡排序
cpp 复制代码
#include <stdio.h>

void bubbleSort(int arr[], int len) {
    for (int i = 0; i < len - 1; i++) {
        // 优化:如果某趟没有交换,说明已有序
        int swapped = 0;
        
        for (int j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                // 交换
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = 1;
            }
        }
        
        // 如果没有交换,提前结束
        if (!swapped) break;
    }
}

int main() {
    int arr[] = {3, 5, 2, 1, 4};
    int len = sizeof(arr) / sizeof(arr[0]);
    
    printf("排序前: ");
    for (int i = 0; i < len; i++) printf("%d ", arr[i]);
    printf("\n");
    
    bubbleSort(arr, len);
    
    printf("排序后: ");
    for (int i = 0; i < len; i++) printf("%d ", arr[i]);
    printf("\n");
    
    return 0;
}
(2)选择排序
cpp 复制代码
#include <stdio.h>

void selectionSort(int arr[], int len) {
    for (int i = 0; i < len - 1; i++) {
        // 假设当前位置是最小值
        int minIndex = i;
        
        // 在后面找到真正的最小值
        for (int j = i + 1; j < len; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        
        // 交换
        if (minIndex != i) {
            int temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
    }
}

int main() {
    int arr[] = {3, 5, 2, 1, 4};
    int len = sizeof(arr) / sizeof(arr[0]);
    
    selectionSort(arr, len);
    
    for (int i = 0; i < len; i++) printf("%d ", arr[i]);
    printf("\n");
    
    return 0;
}
(3)排序算法对比
算法 平均时间 最好时间 最坏时间 空间 稳定性
冒泡排序 O(n²) O(n) O(n²) O(1) 稳定
选择排序 O(n²) O(n²) O(n²) O(1) 不稳定
插入排序 O(n²) O(n) O(n²) O(1) 稳定
快速排序 O(n log n) O(n log n) O(n²) O(log n) 不稳定
归并排序 O(n log n) O(n log n) O(n log n) O(n) 稳定

8、二维数组

8.1 基本概念

维度 数学概念 C语言表示
一维 向量 int arr[5]
二维 矩阵 int arr[3][5]
三维 立方体 int arr[2][3][5]

8.2 定义与初始化

cpp 复制代码
// 方式1:完全初始化
int arr1[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

// 方式2:省略第一维(行数)
int arr2[][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8}
};  // 自动推断为2行

// 方式3:部分初始化
int arr3[3][4] = {
    {1, 2},      // 第1行
    {5, 6, 7},   // 第2行
    {9}          // 第3行
};  // 未初始化的元素为0

// 方式4:一维数组指针数组(不规则二维数组)
int row1[] = {1, 2, 3};
int row2[] = {4, 5, 6, 7, 8};
int row3[] = {9, 10};
int *arr4[] = {row1, row2, row3};  // 每行长度可不同

8.3 内存布局

cpp 复制代码
二维数组在内存中是连续存储的(行优先)

int arr[3][4] 的内存布局:
┌─────┬─────┬─────┬─────┐
│ 0,0 │ 0,1 │ 0,2 │ 0,3 │  ← 第0行
├─────┼─────┼─────┼─────┤
│ 1,0 │ 1,1 │ 1,2 │ 1,3 │  ← 第1行
├─────┼─────┼─────┼─────┤
│ 2,0 │ 2,1 │ 2,2 │ 2,3 │  ← 第2行
└─────┴─────┴─────┴─────┘

地址计算:&arr[i][j] = 首地址 + (i * 列数 + j) * sizeof(元素)

8.4 遍历二维数组

cpp 复制代码
#include <stdio.h>

int main() {
    int arr[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    
    int rows = 3;
    int cols = 4;
    
    // 方式1:普通遍历
    printf("普通遍历:\n");
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%4d", arr[i][j]);
        }
        printf("\n");
    }
    
    // 方式2:指针遍历
    printf("\n指针遍历:\n");
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%4d", *(*(arr + i) + j));
        }
        printf("\n");
    }
    
    return 0;
}

8.5 二维数组作为函数参数

cpp 复制代码
#include <stdio.h>

// 正确:必须指定列数
void printMatrix1(int arr[][4], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%4d", arr[i][j]);
        }
        printf("\n");
    }
}

// 正确:使用指针
void printMatrix2(int (*arr)[4], int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%4d", arr[i][j]);
        }
        printf("\n");
    }
}

// 正确:扁平化处理
void printMatrix3(int *arr, int rows, int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%4d", *(arr + i * cols + j));
        }
        printf("\n");
    }
}

int main() {
    int arr[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    
    printMatrix1(arr, 3);
    printMatrix2(arr, 3);
    printMatrix3(&arr[0][0], 3, 4);
    
    return 0;
}

⚠️ 重要 :二维数组作为参数时,列数必须指定,行数可以省略

8.6 不规则二维数组(指针数组)

cpp 复制代码
#include <stdio.h>

int main() {
    // 每行长度不同的二维数组
    int row1[] = {1, 2, 3};
    int row2[] = {4, 5, 6, 7, 8};
    int row3[] = {9, 10};
    
    // 记录每行的长度
    int lens[] = {3, 5, 2};
    
    // 指针数组
    int *arr[] = {row1, row2, row3};
    int rows = 3;
    
    // 遍历
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < lens[i]; j++) {
            printf("%4d", arr[i][j]);
        }
        printf("\n");
    }
    
    return 0;
}

9、动态数组

9.1 栈数组 vs 堆数组

特性 栈数组 堆数组(动态)
分配位置 栈区 堆区
长度确定 编译时 运行时
生命周期 函数结束自动释放 手动free释放
大小限制 较小(1-8MB) 较大(受内存限制)
分配函数 无需 malloc/calloc
释放函数 自动 free

9.2 动态数组基本用法

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    // 1. 分配内存
    int n = 10;
    int *arr = (int*)malloc(n * sizeof(int));
    
    // 检查分配是否成功
    if (arr == NULL) {
        printf("内存分配失败!\n");
        return 1;
    }
    
    // 2. 使用数组
    for (int i = 0; i < n; i++) {
        arr[i] = i * 10;
    }
    
    // 3. 打印
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    // 4. 释放内存(必须!)
    free(arr);
    arr = NULL;  // 避免悬空指针
    
    return 0;
}

9.3 动态数组函数返回

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

// 函数返回动态数组
int* createArray(int size) {
    int *arr = (int*)malloc(size * sizeof(int));
    if (arr == NULL) {
        return NULL;
    }
    
    for (int i = 0; i < size; i++) {
        arr[i] = i + 1;
    }
    
    return arr;  // 返回堆内存地址
}

int main() {
    int *p = createArray(5);
    
    if (p != NULL) {
        for (int i = 0; i < 5; i++) {
            printf("%d ", p[i]);
        }
        printf("\n");
        
        free(p);  // 调用者负责释放
        p = NULL;
    }
    
    return 0;
}

9.4 动态数组扩容

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    // 初始容量
    int capacity = 5;
    int size = 0;
    int *arr = (int*)malloc(capacity * sizeof(int));
    
    // 添加元素
    for (int i = 0; i < 10; i++) {
        // 容量不足时扩容
        if (size >= capacity) {
            capacity *= 2;  // 容量翻倍
            int *temp = (int*)realloc(arr, capacity * sizeof(int));
            if (temp == NULL) {
                free(arr);
                printf("扩容失败!\n");
                return 1;
            }
            arr = temp;
        }
        
        arr[size++] = i * 10;
    }
    
    // 打印
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    free(arr);
    return 0;
}

9.5 动态二维数组

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>

int main() {
    int rows = 3, cols = 4;
    
    // 分配行指针数组
    int **arr = (int**)malloc(rows * sizeof(int*));
    
    // 分配每行的数据
    for (int i = 0; i < rows; i++) {
        arr[i] = (int*)malloc(cols * sizeof(int));
    }
    
    // 使用
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            arr[i][j] = i * cols + j;
            printf("%4d", arr[i][j]);
        }
        printf("\n");
    }
    
    // 释放(先释放每行,再释放行指针)
    for (int i = 0; i < rows; i++) {
        free(arr[i]);
    }
    free(arr);
    
    return 0;
}

10、常见错误与避坑指南

错误类型 错误示例 正确写法
数组越界 arr[5](长度5) arr[0]arr[4]
未初始化 int arr[5];直接使用 int arr[5] = {0};
参数丢失长度 void func(int arr[]) void func(int arr[], int len)
栈溢出 int big[1000000]; 使用malloc动态分配
忘记free malloc后不释放 配对使用free
二维数组参数 void func(int arr[][]) void func(int arr[][4])
二分查找无序 对无序数组二分 先排序再二分
悬空指针 free(p)后继续使用 free(p); p = NULL;

11、综合练习

11.1 学生成绩管理系统

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

#define MAX_STUDENTS 100

typedef struct {
    int id;
    char name[50];
    float score;
} Student;

// 生成随机成绩
void generateScores(Student students[], int n) {
    srand(time(NULL));
    for (int i = 0; i < n; i++) {
        students[i].id = i + 1;
        sprintf(students[i].name, "学生%d", i + 1);
        students[i].score = rand() % 101;  // 0-100
    }
}

// 计算平均分
float calculateAverage(Student students[], int n) {
    float sum = 0;
    for (int i = 0; i < n; i++) {
        sum += students[i].score;
    }
    return sum / n;
}

// 查找最高分
int findMaxScoreIndex(Student students[], int n) {
    int maxIndex = 0;
    for (int i = 1; i < n; i++) {
        if (students[i].score > students[maxIndex].score) {
            maxIndex = i;
        }
    }
    return maxIndex;
}

// 排序(按成绩降序)
void sortByScore(Student students[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - 1 - i; j++) {
            if (students[j].score < students[j + 1].score) {
                Student temp = students[j];
                students[j] = students[j + 1];
                students[j + 1] = temp;
            }
        }
    }
}

// 打印学生信息
void printStudents(Student students[], int n) {
    printf("%-10s %-20s %-10s\n", "学号", "姓名", "成绩");
    printf("----------------------------------------\n");
    for (int i = 0; i < n; i++) {
        printf("%-10d %-20s %-10.1f\n", 
               students[i].id, students[i].name, students[i].score);
    }
}

int main() {
    Student students[MAX_STUDENTS];
    int n = 10;  // 学生数量
    
    // 生成成绩
    generateScores(students, n);
    
    printf("=== 原始成绩 ===\n");
    printStudents(students, n);
    
    printf("\n平均分: %.2f\n", calculateAverage(students, n));
    
    int maxIndex = findMaxScoreIndex(students, n);
    printf("最高分: %s (%.1f分)\n", 
           students[maxIndex].name, students[maxIndex].score);
    
    printf("\n=== 排序后 ===\n");
    sortByScore(students, n);
    printStudents(students, n);
    
    return 0;
}

相关推荐
To_OC3 小时前
LC 51 N 皇后:我以为难的是回溯,结果栽在了对角线下标
javascript·算法·leetcode
Scott9999HH3 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
2401_841495643 小时前
【操作系统】进程同步与互斥实验报告
c++·算法·操作系统·进程·并发·同步·互斥
码智社4 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海4 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖5 小时前
JDK 26 新特性详解
java·开发语言
马优晨5 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
a1117767 小时前
2FA 验证码生成器(github登录验证 app)
笔记·学习
我的xiaodoujiao7 小时前
API 接口自动化测试详细图文教程学习系列32--Allure测试报告2
python·学习·测试工具·pytest