C语言利用数组处理批量数据的方法
一、引言在实际编程中我们经常需要处理成批的同类型数据比如全班学生的成绩、某城市一年365天的气温、电商网站的商品价格列表等。如果为每个数据单独定义变量如score1,score2, …,score100不仅代码冗长、难以维护而且无法灵活应对数据量变化。C语言提供的数组Array正是解决这类问题的核心工具。它将多个相同类型的元素组织在一块连续的内存区域中通过下标快速访问极大提升了程序对批量数据的处理能力。本讲内容全面覆盖数组的基本原理与内存布局一维/二维/多维数组的声明、初始化与操作常见批量数据处理算法查找、排序、统计、变换典型例题深度解析含边界处理与优化函数中数组的传递机制动态数组与安全实践扩展应用字符串、结构体数组、实际项目场景学习目标掌握使用数组高效处理批量数据的能力理解其底层机制避免常见陷阱为后续学习指针、结构体、文件操作及数据结构打下坚实基础。二、数组的本质与内存模型1. 什么是数组数组是具有相同数据类型的若干元素组成的有序集合这些元素在内存中连续存放每个元素可通过整数下标索引唯一访问。1inta[5] {10, 20, 30, 40, 50};在内存中的布局如下假设int占4字节起始地址为0x1000地址内容下标0x100010a[0]0x100420a[1]0x100830a[2]0x100C40a[3]0x101050a[4]关键点数组名a本质上是首元素的地址即a[0]访问a[i]等价于*(a i)指针算术2. 数组的声明与初始化规则1基本语法1类型说明符 数组名[常量表达式];✅ 合法示例1234#define SIZE 10intarr[SIZE];// 使用宏定义constintn 5;doublevalues[n];// C99 支持 const 变量作大小部分编译器❌ 非法示例12intn 10;intlist[n];// C89 不允许C99 允许变长数组 VLA但有风险2初始化方式初始化形式示例说明完全初始化int a[4] {1,2,3,4};元素个数必须 ≤ 数组大小部分初始化int b[5] {1,2};未初始化元素自动为0自动推断大小int c[] {10,20,30};编译器自动设大小为3全零初始化int d[100] {0};最常用的安全初始化方式建议始终显式初始化数组避免使用未定义值。三、一维数组批量数据的基础操作1. 输入与输出带健壮性检查123456789101112131415161718192021222324252627#include stdio.h#define MAXN 100intmain() {intn, arr[MAXN];printf(请输入数据个数 (≤%d): , MAXN);if(scanf(%d, n) ! 1 || n 0 || n MAXN) {printf(输入无效\n);return1;}printf(请输入 %d 个整数\n, n);for(inti 0; i n; i) {if(scanf(%d, arr[i]) ! 1) {printf(输入错误\n);return1;}}printf(您输入的数据为);for(inti 0; i n; i) {printf(%d , arr[i]);}putchar(\n);return0;}✅健壮性要点检查scanf返回值限制输入数量不超过数组容量提示用户明确输入格式2. 常见批量处理任务1求和、平均值、最值12345678longlongsum 0;// 防止溢出intmin arr[0], max arr[0];for(inti 0; i n; i) {sum arr[i];if(arr[i] min) min arr[i];if(arr[i] max) max arr[i];}doubleavg (double)sum / n;2查找元素顺序查找适用于无序数组12345678910111213inttarget, found -1;printf(请输入要查找的值);scanf(%d, target);for(inti 0; i n; i) {if(arr[i] target) {found i;break;}}if(found ! -1)printf(找到下标为 %d\n, found);elseprintf(未找到\n);二分查找仅适用于已排序数组12345678910111213// 假设 arr 已升序排序intlow 0, high n - 1, mid;while(low high) {mid (low high) / 2;if(arr[mid] target) {printf(找到下标 %d\n, mid);break;}elseif(arr[mid] target) {low mid 1;}else{high mid - 1;}}3排序冒泡排序示例123456789for(inti 0; i n - 1; i) {for(intj 0; j n - 1 - i; j) {if(arr[j] arr[j 1]) {inttemp arr[j];arr[j] arr[j 1];arr[j 1] temp;}}}⏱️复杂度冒泡排序时间复杂度 O(n²)适合小规模数据大规模数据建议用qsort()。四、二维数组表格与矩阵处理1. 声明与内存布局1intmatrix[3][4];// 3行4列内存按行优先Row-major顺序连续存储12matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3],matrix[1][0], matrix[1][1], ..., matrix[2][3]2. 初始化方式1234567891011// 方式1逐行初始化intmat1[2][3] {{1, 2, 3},{4, 5, 6}};// 方式2线性初始化按内存顺序intmat2[2][3] {1, 2, 3, 4, 5, 6};// 方式3部分初始化其余为0intmat3[3][3] {{1}};// 仅 mat3[0][0]1其余为03. 常见操作1矩阵加法12345voidaddMatrix(inta[][COL],intb[][COL],intc[][COL],introws) {for(inti 0; i rows; i)for(intj 0; j COL; j)c[i][j] a[i][j] b[i][j];}2矩阵乘法A: m×n, B: n×p → C: m×p12345678for(inti 0; i m; i) {for(intj 0; j p; j) {c[i][j] 0;for(intk 0; k n; k) {c[i][j] a[i][k] * b[k][j];}}}五、典型例题精讲扩充版例题1学生成绩管理系统一维数组需求输入 N 名学生N ≤ 50的姓名可用学号代替和三门课成绩计算总分、平均分输出排行榜。12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455#include stdio.h#define MAX_STU 50#define SUBJECTS 3intmain() {intn;charnames[MAX_STU][20];// 存储姓名字符串数组intscores[MAX_STU][SUBJECTS];// 成绩二维数组inttotal[MAX_STU] {0};// 总分doubleavg[MAX_STU];printf(请输入学生人数 (≤%d): , MAX_STU);scanf(%d, n);for(inti 0; i n; i) {printf(第 %d 位学生姓名, i 1);scanf(%s, names[i]);printf(三门成绩);for(intj 0; j SUBJECTS; j) {scanf(%d, scores[i][j]);total[i] scores[i][j];}avg[i] (double)total[i] / SUBJECTS;}// 按总分降序排序冒泡for(inti 0; i n - 1; i) {for(intj 0; j n - 1 - i; j) {if(total[j] total[j 1]) {// 交换总分、平均分、姓名、各科成绩intt total[j]; total[j] total[j 1]; total[j 1] t;doublea avg[j]; avg[j] avg[j 1]; avg[j 1] a;chartmp[20];strcpy(tmp, names[j]);strcpy(names[j], names[j 1]);strcpy(names[j 1], tmp);for(intk 0; k SUBJECTS; k) {ints scores[j][k];scores[j][k] scores[j 1][k];scores[j 1][k] s;}}}}printf(\n 成绩排行榜 \n);printf(%-10s %-10s %-10s %-10s %-6s %-6s\n,姓名,语文,数学,英语,总分,平均);for(inti 0; i n; i) {printf(%-10s , names[i]);for(intj 0; j SUBJECTS; j)printf(%-10d , scores[i][j]);printf(%-6d %-6.1f\n, total[i], avg[i]);}return0;}扩展思考若学生人数不确定如何动态分配如何将数据保存到文件能否用结构体简化代码例题2杨辉三角二维数组经典应用要求输出前 N 行杨辉三角。规律第 i 行有 i1 个数两边为1中间a[i][j] a[i-1][j-1] a[i-1][j]123456789101112131415161718192021222324252627#include stdio.h#define MAXN 15intmain() {intn;printf(请输入行数 (≤%d): , MAXN);scanf(%d, n);inttri[MAXN][MAXN] {0};for(inti 0; i n; i) {tri[i][0] tri[i][i] 1;// 首尾为1for(intj 1; j i; j) {tri[i][j] tri[i-1][j-1] tri[i-1][j];}}// 输出居中对齐for(inti 0; i n; i) {for(intk 0; k n - i - 1; k)printf( );for(intj 0; j i; j) {printf(%4d, tri[i][j]);}putchar(\n);}return0;}输出效果n51234511 11 2 11 3 3 11 4 6 4 1例题3筛法求素数埃拉托斯特尼筛思想用布尔数组标记是否为素数逐步筛去合数。1234567891011121314151617181920212223242526#include stdio.h#include stdbool.h#define MAX 1000intmain() {boolisPrime[MAX 1];for(inti 2; i MAX; i) isPrime[i] true;for(inti 2; i * i MAX; i) {if(isPrime[i]) {for(intj i * i; j MAX; j i) {isPrime[j] false;}}}printf(2 到 %d 之间的素数\n, MAX);intcount 0;for(inti 2; i MAX; i) {if(isPrime[i]) {printf(%4d, i);if(count % 10 0)putchar(\n);}}return0;}复制讲解算法优势时间复杂度 O(n log log n)远优于逐个判断。六、数组与函数1. 数组作为参数传递12345678// 一维数组voidprocess(intarr[],intsize);// 等价于 int *arrvoidprocess(int*arr,intsize);// 二维数组必须指定列数voidprint2D(intmat[][4],introws);// 列数4不可省略// 或voidprint2D(int(*mat)[4],introws);// 指针形式重要数组传参传递的是地址函数内修改会影响原数组二维数组形参必须知道列数以便计算偏移