基于AI的C语言程序设计(微课版)课件 第8章 函数实现模块化程序设计_第1页
基于AI的C语言程序设计(微课版)课件 第8章 函数实现模块化程序设计_第2页
基于AI的C语言程序设计(微课版)课件 第8章 函数实现模块化程序设计_第3页
基于AI的C语言程序设计(微课版)课件 第8章 函数实现模块化程序设计_第4页
基于AI的C语言程序设计(微课版)课件 第8章 函数实现模块化程序设计_第5页
已阅读5页,还剩50页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第8章

函数实现模块化程序设计函数驱动模块化主

:蒋亚平目录CONTENTS01函数是什么02函数的定义和调用03数组作为函数参数04递归05图解排序算法06函数综合编程案例07本章小结问题导入解决代码冗余在开发一个学生成绩管理系统时,需要多次计算不同班级的平均分(如计算高一(1)班、(2)班、(3)班的平均分)。如果不用函数,每次计算都重复编写“求和→除以人数”的代码,会出现什么问题?函数如何通过“封装重复逻辑”解决代码冗余和修改繁琐的问题?如何解放main函数开发一个包含登录验证、数据录入、成绩查询、信息修改的多功能程序时,如果所有代码都写在main函数中,会导致什么后果?函数如何通过“按功能分模块”让程序结构更清晰,便于多人协作开发和后期维护?01函数是什么为什么需要函数代码冗余问题在学生成绩管理系统中,若不使用函数,每次计算不同班级平均分都需重复编写求和与除法代码,导致代码冗长、难以维护。函数封装重复逻辑,可有效解决这一问题。模块化优势函数将程序分解为独立模块,如建筑的分工,提高代码可读性与可维护性,便于团队协作开发,是模块化编程的核心工具。函数是什么:生活类比01函数与菜谱类比函数类似于菜谱,输入食材(参数),按步骤加工(执行代码),输出菜肴(返回值)。C语言中,函数是可重用的代码块,可接收参数并返回结果。02借还书流程类比通过“借还书流程”类比,函数定义对应借还规则,参数对应借书证与书名,返回值对应书籍与借阅成功标志,帮助初学者理解抽象概念。03函数的输入输出函数可以有输入参数,也可有返回值。例如,计算两个数之和的函数,接收两个数作为输入,返回它们的和。02函数的定义和调用函数的定义和调用函数的定义和调用返回类型函数名(参数列表){函数体;//功能实现代码return返回值;//返回类型非void时必须有,void无需返回}函数的定义语法接收的输入数据(如intcount),无参数时写()或(void)符合标识符规则函数返回值的类型函数名(实参列表);函数的调用语法函数的定义和调用打印金字塔任务以打印5行金字塔为例,展示如何将任务分解为多个函数:printSpaces打印空格,printStars打印星号,printLine整合两者并换行,printPyramid循环调用printLine。void类型与省略当函数无返回值时,返回类型使用void。在printPyramid函数中,void表明该函数仅执行任务,不返回任何值。示例:实现金字塔形状。函数的定义和调用#include<stdio.h>//函数定义:打印指定数量的空格voidprintSpaces(intcount){for(inti=0;i<count;i++){printf("");}}//函数定义:打印指定数量的星号voidprintStars(intcount){for(inti=0;i<count;i++){printf("*");}}//函数定义:打印金字塔的一行voidprintLine(intline,inttotalLines){intspaces=totalLines-line;intstars=2*line-1;printSpaces(spaces);//调用打印空格的函数printStars(stars); //调用打印星号的函数printf("\n");//换行}运行结果://函数定义:打印完整的金字塔voidprintPyramid(inttotalLines){for(inti=1;i<=totalLines;i++){printLine(i,totalLines);//调用打印一行的函数}}intmain(){intn=5; //金字塔的行数printf("金字塔形状:\n");printPyramid(n); //调用打印金字塔的函数return0;}调用链与参数匹配调用链与参数匹配main函数调用printPyramid(5)启动调用链,printPyramid循环调用printLine(i,5),printLine再调用printSpaces与printStars。实参与形参类型、数量必须一致,否则编译错误。03数组作为函数参数数组作为函数参数一维数组名作为参数一维数组名作为函数参数的内存存储示意图函数定义时,形参的写法有两种函数名(类型

数组名[])(推荐,直观表示数组)函数名(类型*指针名)(本质与数组名等价,因数组名即地址)voidfunc(intarr[]);voidfunc(int*arr);一维数组名作为参数示例:计算一维数组元素的平均值。#include<stdio.h>//函数:计算数组元素的平均值(形参用数组形式)floatcalculateAverage(intarr[],intlength){intsum=0;for(inti=0;i<length;i++){sum+=arr[i];//通过数组下标访问元素}return(float)sum/length;//转换为浮点型返回}运行结果:intmain(){intnumbers[]={10,20,30,40,50};//计算数组长度intlen=sizeof(numbers)/sizeof(numbers[0]);//传递数组名和长度floatavg=calculateAverage(numbers,len); printf("数组元素:");for(inti=0;i<len;i++){printf("%d",numbers[i]);}printf("\n平均值:%.2f\n",avg);return0;}AI辅助:

必须传递数组长度:数组作为参数时需手动传递长度,避免遍历越界;地址传递特性:数组传参为地址传递,函数内修改将同步到原数组二维数组的行地址作为参数列数必须指定二维数组传参时,形参必须给出列数,例如,intarr[][3],以便编译器计算arr[i][j]的偏移。第一维可省略,但第二维必须明确。地址计算与错误若列数不匹配,地址计算错误将导致数据错乱。例如,matrix[3][3]实参与arr[][3]形参耦合时,列数必须一致。第一维可省略,但第二维必须明确。AI辅助:

实参二维数组与形参的列数必须完全一致!比如实参是intmatrix[3][3](3行3列),形参就必须定义为intarr[][3](列数3不能改),若写成intarr[][2],列数不匹配会直接导致地址计算错误,程序运行异常。二维数组的行地址作为参数示例:计算二维数组每行元素的和。#include<stdio.h>//函数:计算二维数组每行的元素和(形参指定列数为3)voidcalculateRowSum(intarr[][3],introws,intcols){for(inti=0;i<rows;i++){intsum=0;for(intj=0;j<cols;j++){sum+=arr[i][j];//访问第i行第j列元素}printf("第%d行元素之和:%d\n",i+1,sum);}}运行结果:intmain(){//定义3行3列的二维数组intmatrix[3][3]={{1,2,3},{4,5,6},{7,8,9}};introws=3,cols=3;printf("二维数组元素:\n");for(inti=0;i<rows;i++){for(intj=0;j<cols;j++){printf("%d",matrix[i][j]);}printf("\n");}//传递二维数组名(行地址的起始)calculateRowSum(matrix,rows,cols);return0;}04递归递归递归递归:德罗斯特效应递归是一种非常强大的编程技术,它允许函数调用自身。在递归过程中,函数会不断地调用自己,直到达到某个终止条件为止。德罗斯特效应(Drosteeffect)是递归的一种视觉形式,指一张图片的某个部分与整张图片相同,产生无限循环的效果。递归双要素:基线与递推01基线条件递归函数必须有基线条件终止无限自我调用。例如,斐波那契数列中,n==0或1时直接返回n,避免栈溢出。02递推条件递归函数需将大问题拆成同构子问题。斐波那契数列中,F(n)=F(n-1)+F(n-2),不断拆分至基线。03递归的思想递归的思想类似于数学中的归纳法,它将一个复杂的问题分解为一个或多个相似但规模更小的子问题。斐波那契数列示例:递归实现斐波那契数列。斐波那契数列是一个经典的数学序列,它的定义如下:(1)第0项为0(2)第1项为1(3)从第2项开始,每一项都是前两项的和,即:F(n)=F(n-1)+F(n-2)#include<stdio.h>//递归函数:计算斐波那契数列的第n项intfibonacci(intn){//基线条件if(n==0){return0;}if(n==1){return1;}//递归条件returnfibonacci(n-1)+fibonacci(n-2);}intmain(){intn=10;//计算前10个斐波那契数printf("斐波那契数列前%d项:\n",n);for(inti=0;i<n;i++){printf("%d",fibonacci(i));}printf("\n");return0;}数字全排列示例:递归实现数字全排列。数字全排列是指将给定的n个数字进行所有可能的排列。例如,对于数字1、2、3,它们的全排列有:123、132、213、231、312、321。//递归函数:生成从start到end的全排列voidpermute(intarr[],intstart,intend){if(start==end){//基线条件:当start等于end时,输出当前排列for(inti=0;i<=end;i++){printf("%d",arr[i]);}printf("\n");}else{//递归条件:生成所有可能的排列for(inti=start;i<=end;i++){swap(&arr[start],&arr[i]);//交换当前元素permute(arr,start+1,end);//递归生成后续元素的排列swap(&arr[start],&arr[i]);//回溯,恢复原来的顺序}}}intmain(){intarr[]={1,2,3};intn=sizeof(arr)/sizeof(arr[0]);printf("数字全排列:\n");permute(arr,0,n-1);return0;}运行结果:#include<stdio.h>//交换两个元素的值voidswap(int*a,int*b){inttemp=*a;*a=*b;*b=temp;}基线条件递归条件回溯主函数交换函数最大公约数与最小公倍数示例:使用递归实现计算

“最大公约数和最小公倍数”。最大公约数(GCD)是指两个或多个整数共有约数中最大的一个。例如,12和18的最大公约数是6。最小公倍数(LCM)是指两个或多个整数公有的倍数中最小的一个,最小公倍数

=两数的乘积/最大公约数。例如,12和18的最小公倍数是36。计算最大公约数和最小公倍数有多种方法,其中最常用的是欧几里得算法(辗转相除法)。#include<stdio.h>//递归函数:计算最大公约数intgcd(inta,intb){//基线条件:当b为0时,返回aif(b==0){returna;}//递归条件:递归计算b和a%b的最大公约数returngcd(b,a%b);}//函数:计算最小公倍数intlcm(inta,intb){return(a*b)/gcd(a,b);}intmain(){intnum1=12,num2=18;printf("数字%d和%d的最大公约数是:%d\n",num1,num2,gcd(num1,num2));printf("数字%d和%d的最小公倍数是:%d\n",num1,num2,lcm(num1,num2));return0;}基线和递归条件最小公倍数杨辉三角示例:使用递归实现生成杨辉三角。杨辉三角(也称为帕斯卡三角)是一个由数字排列成的三角形,其特点是每个数等于它上方两数之和。//函数:打印杨辉三角的前n行voidprintPascalTriangle(intn){for(inti=0;i<n;i++){//打印前导空格,使杨辉三角居中显示for(intj=0;j<n-i-1;j++){printf("");}//打印当前行的数值for(intj=0;j<=i;j++){printf("%d",binomialCoefficient(i,j));}printf("\n");}}intmain(){intn=10;//打印前10行printf("杨辉三角的前%d行:\n",n);printPascalTriangle(n);return0;}#include<stdio.h>//递归函数:计算杨辉三角中第row行第col列的值intbinomialCoefficient(introw,intcol){//基线条件:当col为0或col等于row时,返回1if(col==0||col==row){return1;}//递归条件:计算上一行中两个数的和returnbinomialCoefficient(row-1,col-1)+binomialCoefficient(row-1,col);}05图解排序算法插入排序插入排序基本思想插入排序是一种简单的排序算法。它将待排序的元素逐个插入到已经排好序的部分数组中,直到整个数组有序。插入排序通常适用于数据量较小或接近有序的情况。组成部分在插入排序中,我们将数组分为两部分:已经排序好的部分和未排序部分。每次从未排序部分选择一个元素,并将其插入到已排序部分的适当位置。直到整个数组排序完成。直接插入排序01直接插入排序插入排序主要分为二种:直接插入排序和折半插入排序。直接插入排序(StraightInsertionSort)是一种最基本的插入排序方法。它的主要思路是:从第二个元素开始,逐步将每个元素插入到前面的已排序部分中。每次插入时,从后向前扫描已排序部分,直到找到插入位置。直接插入排序的过程直接插入排序示例:直接插入排序。//测试插入排序intmain(){intarr[]={12,11,13,5,6};intlength=sizeof(arr)/sizeof(arr[0]);//获取数组长度printf("排序前:");for(inti=0;i<length;i++){//使用传统的for循环printf("%d",arr[i]);}insertionSort(arr,length);//传递数组和长度printf("\n排序后:");for(inti=0;i<length;i++){//使用传统的for循环printf("%d",arr[i]);}return0;}#include<stdio.h>voidinsertionSort(intarr[],intlength){//从第二个元素开始,假设第一个元素是已排序的for(inti=1;i<length;i++){//取出当前元素intcurrentElement=arr[i];intj=i-1;//向左移动已排序部分的元素,

//直到找到当前元素应插入的位置while(j>=0&&arr[j]>currentElement){arr[j+1]=arr[j];//右移元素j--;}//将当前元素插入到找到的位置arr[j+1]=currentElement;}}运行结果:折半插入排序02折半插入排序插入排序主要分为二种:直接插入排序和折半插入排序。折半插入排序(BinaryInsertionSort)是一种改进的插入排序,它通过使用二分查找来找到合适的位置插入元素,从而减少了元素比较的次数。与直接插入排序不同,折半插入排序使用二分查找来确定插入位置,但插入操作仍然是通过向右移动元素来完成的。插入排序示意图给定一个包含n个元素的序列,其中前i个元素已经是有序的,现在需要将下标为i的元素插入到正确的位置。折半插入排序02折半插入排序插入排序主要分为二种:直接插入排序和折半插入排序。“折半”示意图折半插入排序分为两步:一是找到待插入元素的位置,二是将其插入。折半插入排序的核心思想是利用二分法在有序序列a[0]到a[i-1]中找到a[i]应该插入的位置。1)比较a[i]和a[mid]的大小。如果a[i]>a[mid],说明a[i]应该插入到mid之后,更新low=mid+1,即将搜索范围缩小到[mid+1,high]。如果a[i]<=a[mid],说明a[i]应该插入到mid之前,更新high=mid-1,即将搜索范围缩小到[low,mid-1]。2)重复上述过程,不断调整low和high,直到low大于high时跳出循环,此时low即为a[i]应该插入的位置。找到插入位置后,我们将a[low]到a[i-1]之间的元素依次后移一个位置,然后将a[i]插入到low位置。折半插入排序示例:折半插入排序。//将已排序部分的元素后移,为当前元素腾出位置for(intj=i;j>insertPosition;j--){arr[j]=arr[j-1];}//将当前元素插入到合适的位置arr[insertPosition]=currentElement;}}//测试折半插入排序intmain(){intarr[]={12,11,13,5,6};intlength=sizeof(arr)/sizeof(arr[0]);//获取数组长度printf("排序前:");for(inti=0;i<length;i++){printf("%d",arr[i]);}binaryInsertionSort(arr,length);//调用折半插入排序printf("\n排序后:");for(inti=0;i<length;i++){printf("%d",arr[i]);}return0;}#include<stdio.h>//二分查找函数,用于在已排序部分找到插入位置intbinarySearch(intarr[],inttarget,intlow,inthigh){while(low<=high){intmid=low+(high-low)/2;if(arr[mid]==target){returnmid;}elseif(arr[mid]<target){low=mid+1;}else{high=mid-1;}}returnlow;//返回插入的位置}//折半插入排序函数voidbinaryInsertionSort(intarr[],intlength){for(inti=1;i<length;i++){intcurrentElement=arr[i];//使用二分查找来找到当前元素的插入位置intinsertPosition=binarySearch(arr,currentElement,0,i-1);

交换排序交换排序:冒泡排序01冒泡排序常见的交换排序算法有冒泡排序和快速排序。冒泡排序(BubbleSort)是一种简单的交换排序算法。其基本思想是:通过多次遍历数组,将相邻元素两两比较并交换位置,使得每一轮遍历后,最大(或最小)的元素“冒泡”到数组的末端。这个过程会重复进行,直到整个数组有序。交换排序的基本思想交换排序是基于比较的排序算法,排序过程中通过交换元素的位置来使得数组有序。交换排序:冒泡排序冒泡排序的过程冒泡排序的基本步骤(以升序为例)

从第一个元素开始,依次比较相邻的两个元素。

如果当前元素大于下一个元素,则交换它们的位置。

每一次遍历结束后,当前未排序部分中的最大元素被交换到最后。

重复步骤1到步骤3,直到整个数组有序。最大值:90交换排序:冒泡排序示例:冒泡排序。//测试冒泡排序intmain(){intarr[]={64,34,25,12,22,11,90};intlength=sizeof(arr)/sizeof(arr[0]);//获取数组长度printf("排序前:");for(inti=0;i<length;i++){//使用传统的for循环遍历数组printf("%d",arr[i]);}bubbleSort(arr,length);//调用冒泡排序printf("\n排序后:");for(inti=0;i<length;i++){//使用传统的for循环遍历数组printf("%d",arr[i]);}return0;}#include<stdio.h>//冒泡排序函数voidbubbleSort(intarr[],intlength){//外循环控制排序轮数for(inti=0;i<length-1;i++){//内循环执行每一轮的比较与交换for(intj=0;j<length-1-i;j++){if(arr[j]>arr[j+1]){//交换位置inttemp=arr[j];arr[j]=arr[j+1];arr[j+1]=temp;}}}}运行结果:交换排序:快速排序02快速排序快速排序(QuickSort)是由冒泡排序改进而得的,它是一种分治法的排序算法,它通过一个“分区”操作将数组分为两部分,并递归地排序这两部分,最终达到整个数组有序。快速排序通常被认为是最优的交换排序算法,尤其在处理大数据时表现出色。交换排序:快速排序快速排序第一轮操作过程快速排序第一轮操作的基本步骤1、第一个元素作为基准数,左侧下标用left表示,右侧下标用right表示。2、分别从序列两端出发。第一次交换:从右侧向左寻找小于6的数字(找到5),接着从左侧向右寻找大于6的数字(找到7),交换数字7和数字5,此时完成第一次交换第二次交换:right继续向左移动;left继续向右移动。right和left再次分别向左和向右移动,依次类推,完成第一轮操作。3、左侧子序列和右侧子序列继续进行相同的操作。AI辅助:每次必须right先出发。交换排序:快速排序快速排序算法的完整过程快速排序的基本步骤选择一个“基准”元素,一般选择数组的第一个、最后一个或中间的元素。对数组进行一次分区操作,使得所有小于基准元素的值都排在基准元素的左边,所有大于基准元素的值都排在基准元素的右边。递归地对基准元素左右的子数组进行快速排序,直到子数组的大小为1。交换排序:快速排序示例:快速排序。#include<stdio.h>//交换数组中两个元素的位置voidswap(intarr[],inti,intj){inttemp=arr[i];arr[i]=arr[j];arr[j]=temp;}//分区函数,返回基准元素的位置intpartition(intarr[],intlow,inthigh){//选择最左边的元素作为基准intpivot=arr[low];intleft=low+1;intright=high;while(1){//向右移动,直到找到大于或等于基准的元素while(left<=right&&arr[left]<=pivot){left++;}//向左移动,直到找到小于或等于基准的元素while(left<=right&&arr[right]>=pivot){right--;}

//如果左指针小于右指针,则交换if(left<right){swap(arr,left,right);}else{break;}}//把基准元素放到正确的位置swap(arr,low,right);returnright;}//快速排序主函数voidquickSort(intarr[],intlow,inthigh){if(low<high){//找到基准元素的位置intpivotIndex=partition(arr,low,high);

//递归排序基准元素左侧和右侧的子数组quickSort(arr,low,pivotIndex-1);quickSort(arr,pivotIndex+1,high);}}//主函数,用于测试intmain(){intarr[]={6,1,2,7,9,3,4,5,10,8};//获取数组长度intlength=sizeof(arr)/sizeof(arr[0]);printf("原始数组:\n");for(inti=0;i<length;i++){printf("%d",arr[i]);}//调用快速排序quickSort(arr,0,length-1);printf("\n排序后的数组:\n");for(inti=0;i<length;i++){printf("%d",arr[i]);}

return0;}选择排序选择排序选择排序(SelectionSort)是一种简单的排序算法,其基本思想是每一轮遍历数组,选择一个最小(或最大)的元素放到已排序部分的末尾。选择排序的基本思想:

从未排序的部分中找到最小(或最大)元素。

将最小(或最大)元素与未排序部分的第一个元素交换位置。

将已排序部分和未排序部分分开,并继续处理未排序部分。

重复步骤1和步骤2,直到所有元素有序。选择排序选择排序的过程选择排序的步骤:

初始阶段:开始时未排序部分是整个数组,已排序部分为空。

第一轮遍历:找出未排序部分中的最小(或最大)元素,并将其与当前未排序部分的第一个元素交换。

第二轮遍历:在未排序部分剩余元素中找出最小(或最大)元素,交换到第二个位置。

重复:继续找出未排序部分的最小(或最大)元素,交换直到所有元素排序完成。选择排序运行结果://测试选择排序intmain(){intarr[]={64,25,12,22,11};intlength=sizeof(arr)/sizeof(arr[0]);//获取数组长度printf("排序前:");for(inti=0;i<length;i++){//使用传统的for循环遍历数组printf("%d",arr[i]);}selectionSort(arr,length);//调用选择排序printf("\n排序后:");for(inti=0;i<length;i++){//使用传统的for循环遍历数组printf("%d",arr[i]);}return0;}#include<stdio.h>//选择排序函数voidselectionSort(intarr[],intlength){//遍历整个数组for(inti=0;i<length-1;i++){//假设当前索引i是未排序部分的最小值的索引intminIndex=i;//从未排序的部分找到最小值for(intj=i+1;j<length;j++){if(arr[j]<arr[minIndex]){minIndex=j;//更新最小值的索引}}//如果最小值的索引不是当前索引i,交换它们if(minIndex!=i){inttemp=arr[i];arr[i]=arr[minIndex];arr[minIndex]=temp;}}}示例:选择排序。运行结果:排序对比算法选型建议

稳定性标注

代码优化小规模数据用直接插入排序,大规模数据用快速排序;冒泡、插入排序稳定,快速、选择排序不稳定;对于冒泡排序,添加标志位,若某轮无交换则数组已有序,提前终止循环。06函数综合编程案例函数综合编程案例综合编程案例#include<stdio.h>//求数组最大值intgetMax(intarr[],intlen){intmax=arr[0];for(inti=1;i<len;i++){if(arr[i]>max)max=arr[i];}returnmax;}//求数组最小值intgetMin(intarr[],intlen){intmin=arr[0];for(inti=1;i<len;i++){if(arr[i]<min)min=arr[i];}returnmin;}案例一:求数组元素的最大值和最小值(函数处理批量数据)。运行结果:

intmain(){intnums[]={12,45,7,99,33,61};

//计算数组长度intlength=sizeof(nums)/sizeof(nums[0]);intmax=getMax(nums,length);intmin=getMin(nums,length);printf("数组元素:");for(inti=0;i<length;i++){printf("%d",nums[i]);}printf("\n最大值:%d,最小值:

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论