C程序设计(第五版)第6章 利用数组处理批量数据:知识要点、案例分析与课后习题解析

📅 2026/8/21 3:58:40
C程序设计(第五版)第6章 利用数组处理批量数据:知识要点、案例分析与课后习题解析
一、本章导学在之前的学习中我们接触的都是简单变量——一个变量同一时刻只能保存一个数据。但在实际编程里我们经常需要批量处理数据例如求全班 50 名学生的平均成绩、对 100 个数排序、统计一段文本中每个字母出现的次数。如果为每个数据都单独声明一个变量代码会冗长且难以维护。《C程序设计第五版》第 6 章引入的数组正是为了解决这类问题。本章的核心内容包括一维数组、二维数组和字符数组的定义、初始化、引用方式以及基于数组的经典算法排序、矩阵运算、字符串处理等。学习目标掌握数组的定义与初始化规则理解数组下标从 0 开始的约定及越界的危害能独立写出冒泡排序、矩阵转置、杨辉三角等经典程序熟练使用strcpy、strcat、strcmp、strlen等字符串函数。二、知识要点梳理2.1 一维数组1. 一维数组的定义定义格式类型说明符 数组名[常量表达式];示例inta[10];// 定义包含 10 个整型元素的数组floatscore[50];// 保存 50 名学生的成绩charch[26];// 保存 26 个字符注意要点方括号中的表达式必须为常量表达式不能是变量。以下写法是错误的intn10;inta[n];// 错误传统 C89 标准下不允许数组名为常量地址表示数组首元素的地址不能被赋值。2. 一维数组的初始化inta[5]{1,2,3,4,5};// 全部初始化intb[5]{1,2};// 前两个为 1、2其余自动补 0intc[]{1,2,3};// 自动确定长度为 3staticintd[5];// 静态数组默认全部为 0易错点数组元素越界虽不会报语法错误但会访问到非法内存可能引发难以预料的运行时错误。例如inta[5];a[5]10;// 越界合法下标是 0~43. 一维数组的引用数组必须逐个元素使用不能整体引用inta[5]{1,2,3,4,5};printf(%d,a[0]);// 正确输出第 1 个元素printf(%d,a);// 错误输出的是地址2.2 二维数组1. 二维数组的定义类型说明符 数组名[常量表达式1][常量表达式2];示例inta[3][4];// 3 行 4 列共 12 个元素二维数组在内存中是按行存放的即先存第 0 行再存第 1 行……可以把二维数组理解为“数组的数组”。2. 二维数组的初始化inta[3][4]{{1,2,3,4},{5,6,7,8},{9,10,11,12}};// 分行初始化intb[3][4]{1,2,3,4,5,6,7,8,9,10,11,12};// 按行顺序初始化intc[3][4]{{1},{0,5},{9}};// 未赋值元素自动为 0intd[][3]{1,2,3,4,5,6};// 可省略第一维长度3. 二维数组的引用a[2][3]20;// 引用第 3 行第 4 列元素2.3 字符数组与字符串1. 字符数组的定义与初始化charc[5]{h,e,l,l,o};// 字符数组chars[]hello;// 字符串形式实际占 6 字节字符串形式初始化时系统自动在后面加上结束标志\0因此hello占用 6 个字节而非 5 个。2. 字符串的输入输出charstr[20];scanf(%s,str);// 以字符串形式输入遇空格结束无需 printf(%s,str);// 以字符串形式输出gets(str);// 读取一行可含空格puts(str);// 输出字符串并自动换行注意scanf以空格、回车作为输入分隔无法读入带空格的字符串gets可读入整行但存在缓冲区溢出风险现代编译器常提示使用fgets替代。3. 常用字符串处理函数string.h函数功能strlen(s)返回字符串长度不含\0strcpy(s1, s2)把 s2 复制到 s1strcat(s1, s2)把 s2 连接到 s1 后面strcmp(s1, s2)按字典序比较返回正数/0/负数#includestdio.h#includestring.hintmain(){chara[20]Hello;charb[20]World;printf(len %d\n,(int)strlen(a));// 5strcat(a,b);printf(%s\n,a);// HelloWorldif(strcmp(a,b)0)printf(a b\n);return0;}三、案例分析案例 1冒泡排序问题对 10 个整数按从小到大排序。冒泡排序的核心思想每趟比较相邻两数若顺序不对则交换。经过 n-1 趟后序列有序。#includestdio.h#defineN10intmain(){inta[N]{34,12,56,78,25,99,45,61,3,17};inti,j,temp;printf(排序前);for(i0;iN;i)printf(%4d,a[i]);printf(\n);/* 冒泡排序 */for(i0;iN-1;i){// 共 N-1 趟for(j0;jN-1-i;j){// 每趟比较次数递减if(a[j]a[j1]){// 相邻比较前面大则交换tempa[j];a[j]a[j1];a[j1]temp;}}}printf(排序后);for(i0;iN;i)printf(%4d,a[i]);printf(\n);return0;}输出排序前 34 12 56 78 25 99 45 61 3 17 排序后 3 12 17 25 34 45 56 61 78 99要点解析外层循环控制趟数n 个数最多需要 n-1 趟内层循环j N - 1 - i因为每趟都会“冒”出一个最大值到末尾已就位的元素无需再比较。案例 2求 3×3 矩阵对角线元素之和问题求一个 3×3 整数矩阵主对角线元素之和。#includestdio.hintmain(){inta[3][3]{{1,2,3},{4,5,6},{7,8,9}};inti,sum0;for(i0;i3;i)sumsuma[i][i];// 主对角线元素下标行 列printf(主对角线元素之和 %d\n,sum);return0;}输出主对角线元素之和 15拓展副对角线元素的特点是i j n - 1读者可自行尝试求副对角线之和。案例 3杨辉三角问题打印杨辉三角的前 10 行。杨辉三角规律首尾元素为 1其余元素等于其上方元素与左上方元素之和。#includestdio.h#defineN10intmain(){inta[N][N]{0};// 先全部初始化为 0inti,j;for(i0;iN;i){a[i][0]1;// 每行第一个元素为 1a[i][i]1;// 每行最后一个元素为 1for(j1;ji;j)a[i][j]a[i-1][j-1]a[i-1][j];}for(i0;iN;i){for(j0;ji;j)printf(%6d,a[i][j]);printf(\n);}return0;}输出前 10 行1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 ...案例 4字符串逆序问题输入一个字符串将其逆序后输出。思路用两个下标i从头、j从尾相向而行交换对应字符直到相遇。#includestdio.h#includestring.hintmain(){charstr[100];inti,j,len;chartemp;printf(请输入一个字符串);gets(str);len(int)strlen(str);i0;jlen-1;while(ij){tempstr[i];str[i]str[j];str[j]temp;i;j--;}printf(逆序后%s\n,str);return0;}四、课后习题解析下面挑选本章几道典型课后习题进行解析。习题 1用筛选法求 100 之内的素数思路筛选法埃拉托斯特尼筛法。先把 2~100 全部标记为“候选素数”从 2 开始把它的所有倍数筛掉再找下一个未被筛掉的数必为素数依次进行。#includestdio.h#includemath.hintmain(){inti,j,n,a[101];for(i1;i100;i)a[i]i;// 存入 1~100a[1]0;// 1 不是素数挖掉for(i2;isqrt(100);i){for(ji1;j100;j){if(a[i]!0a[j]!0)if(a[j]%a[i]0)a[j]0;// 挖掉非素数}}printf(100 以内素数\n);for(i2;i100;i)if(a[i]!0)printf(%d ,a[i]);printf(\n);return0;}输出2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97习题 2已排序数组插入一个数保持有序问题有 10 个从小到大排好序的整数现输入一个数插入后数组仍有序。思路先找到插入位置再将插入点之后的元素逐个后移最后放入新数。#includestdio.hintmain(){inta[11]{2,5,8,10,15,20,25,30,35,40};intnum,i,j;printf(输入要插入的数);scanf(%d,num);/* 找插入位置第一个比 num 大的元素位置 */for(i0;i10;i)if(a[i]num)break;/* 后移元素 */for(j10;ji;j--)a[j]a[j-1];a[i]num;// 插入新数printf(插入后数组\n);for(j0;j11;j)printf(%d ,a[j]);printf(\n);return0;}习题 3用选择法对 10 个整数排序问题教材常要求分别用选择法与冒泡法排序这里演示选择法。思路每一趟从未排序部分选出最小值与未排序部分的第一个元素交换。#includestdio.h#defineN10intmain(){inta[N]{45,12,89,3,56,78,21,4,67,30};inti,j,min,temp;for(i0;iN-1;i){mini;// 假设当前趟第一个元素最小for(ji1;jN;j)if(a[j]a[min])minj;// 记录更小元素下标if(min!i){// 若最小值不在首位则交换tempa[i];a[i]a[min];a[min]temp;}}printf(排序结果\n);for(i0;iN;i)printf(%d ,a[i]);printf(\n);return0;}五、本章易错点与学习建议数组下标越界C 语言不检查下标合法性编程时务必保证下标在0 ~ n-1范围内。字符串结束标志\0用字符串形式初始化时长度会自动 1计算存储空间时不要忽略。scanf输入字符串数组名前不需要加且%s遇空格即停止。数组不能整体赋值a b;是正确的吗不是必须用循环或strcpy字符数组逐个元素复制。多写多练数组是本课程的分水岭之一排序、矩阵、杨辉三角等经典题建议独立手写 2~3 遍才能真正内化为自己的编程能力。六、小结本章以“批量数据处理”为主线层层推进从一维数组的基本用法到二维数组的矩阵处理再到字符数组与字符串函数的应用。学好数组不仅是为后续的指针、函数和数据结构打基础更是在训练“用循环批量处理数据”这一核心编程思维。建议读者在掌握语法之后重点完成以下几类实战练习检验学习效果基础求一维数组最大值/最小值、求和、求平均值排序冒泡排序、选择排序矩阵矩阵转置、对角线元素、行列互换字符串回文判断、字符统计、大小写转换、字符串比较。把每一个案例都在本地环境运行一遍、调试一遍你会发现数组的威力远超想象。