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 数组特点

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

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;
}

相关推荐
不吃橘子的橘猫1 小时前
《集成电路设计》复习资料4(Verilog HDL概述)
学习·算法·fpga开发·集成电路·仿真·半导体
楼田莉子1 小时前
Linux学习:线程的同步与互斥
linux·运维·c++·学习
xyq20242 小时前
空对象模式
开发语言
宇木灵2 小时前
C语言基础-三、流程控制语句
java·c语言·前端
xsyaaaan2 小时前
代码随想录Day39动态规划:115不同的子序列_583两个字符串的删除操作_72编辑距离_编辑距离总结
算法·动态规划
陈天伟教授2 小时前
人工智能应用- 人工智能交叉:05. 从 AlphaFold1 到 AlphaFold2
人工智能·神经网络·算法·机器学习·推荐算法
Eloudy3 小时前
直接法 读书笔记 05 第5章 正交方法
人工智能·算法·机器学习
iAkuya3 小时前
(leetcode)力扣100 73柱状图中最大的矩形(单调栈)
算法·leetcode·职场和发展
pp起床3 小时前
动态规划 | part03
算法·动态规划