news 2026/2/10 12:13:41

CSP-J教程——第一阶段第十一课:函数与递归初步

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CSP-J教程——第一阶段第十一课:函数与递归初步

课程目标

  • 理解函数的概念和优势
  • 掌握函数的定义、声明和调用
  • 理解参数传递(值传递)的机制
  • 理解返回值的意义和使用
  • 初步了解递归的概念
  • 学会将复杂问题模块化

第一部分:函数的基本概念(40分钟)

1.1 什么是函数?

生活比喻:

  • 厨房电器:微波炉、烤箱 - 输入食材,输出熟食,我们不需要知道内部原理
  • 快递员:把包裹(参数)交给快递员,他负责送到目的地(返回值)
  • 工厂生产线:每个工人(函数)负责特定的工序

1.2 为什么需要函数?

没有函数的困境:

#include<iostream>usingnamespacestd;intmain(){// 计算圆的面积(重复代码)doubleradius1=5.0;doublearea1=3.14159*radius1*radius1;cout<<"圆1的面积: "<<area1<<endl;doubleradius2=3.0;doublearea2=3.14159*radius2*radius2;cout<<"圆2的面积: "<<area2<<endl;doubleradius3=7.0;doublearea3=3.14159*radius3*radius3;cout<<"圆3的面积: "<<area3<<endl;return0;}

使用函数的便利:

#include<iostream>usingnamespacestd;// 定义计算圆面积的函数doublecalculateCircleArea(doubleradius){return3.14159*radius*radius;}intmain(){// 使用函数计算圆的面积cout<<"圆1的面积: "<<calculateCircleArea(5.0)<<endl;cout<<"圆2的面积: "<<calculateCircleArea(3.0)<<endl;cout<<"圆3的面积: "<<calculateCircleArea(7.0)<<endl;return0;}

1.3 函数的优势

  1. 代码复用:避免重复编写相同代码
  2. 模块化:将复杂问题分解为小问题
  3. 可读性:函数名可以描述功能,代码更易理解
  4. 易于维护:修改功能只需修改一个地方
  5. 团队协作:不同程序员可以负责不同函数

第二部分:函数的定义和调用(60分钟)

2.1 函数的组成部分

函数定义语法:

返回类型 函数名(参数列表){// 函数体return返回值;// 如果返回类型不是void}

2.2 无参数无返回值的函数

#include<iostream>usingnamespacestd;// 函数定义:显示欢迎信息voidshowWelcome(){cout<<"****************"<<endl;cout<<"* 欢迎使用! *"<<endl;cout<<"****************"<<endl;}// 函数定义:显示菜单voidshowMenu(){cout<<"\n=== 菜单 ==="<<endl;cout<<"1. 开始游戏"<<endl;cout<<"2. 设置"<<endl;cout<<"3. 退出"<<endl;cout<<"============"<<endl;}intmain(){// 函数调用showWelcome();showMenu();cout<<"程序继续执行..."<<endl;// 可以多次调用同一个函数showMenu();return0;}

2.3 带参数无返回值的函数

#include<iostream>usingnamespacestd;// 函数定义:显示指定次数的消息voidshowMessage(string message,inttimes){for(inti=0;i<times;i++){cout<<message<<endl;}}// 函数定义:显示个人信息voidshowPersonInfo(string name,intage,doubleheight){cout<<"\n=== 个人信息 ==="<<endl;cout<<"姓名: "<<name<<endl;cout<<"年龄: "<<age<<"岁"<<endl;cout<<"身高: "<<height<<"米"<<endl;}// 函数定义:打印指定行数和列数的图案voidprintPattern(charsymbol,introws,intcols){for(inti=0;i<rows;i++){for(intj=0;j<cols;j++){cout<<symbol<<" ";}cout<<endl;}}intmain(){// 调用带参数的函数showMessage("Hello, Function!",3);showPersonInfo("小明",12,1.65);printPattern('*',4,6);return0;}

2.4 带返回值的函数

#include<iostream>usingnamespacestd;// 函数定义:计算两个数的和intadd(inta,intb){intresult=a+b;returnresult;// 返回计算结果}// 函数定义:判断一个数是否为偶数boolisEven(intnumber){returnnumber%2==0;// 直接返回布尔表达式的结果}// 函数定义:计算阶乘intfactorial(intn){intresult=1;for(inti=1;i<=n;i++){result*=i;}returnresult;}// 函数定义:获取成绩等级chargetGrade(intscore){if(score>=90)return'A';elseif(score>=80)return'B';elseif(score>=70)return'C';elseif(score>=60)return'D';elsereturn'E';}intmain(){// 使用带返回值的函数intsum=add(5,3);cout<<"5 + 3 = "<<sum<<endl;// 直接在表达式中使用函数调用cout<<"10 + 15 = "<<add(10,15)<<endl;// 使用判断函数intnum=7;if(isEven(num)){cout<<num<<" 是偶数"<<endl;}else{cout<<num<<" 是奇数"<<endl;}// 使用计算函数cout<<"5的阶乘 = "<<factorial(5)<<endl;// 使用等级函数intscore=85;cout<<"成绩 "<<score<<" 的等级是: "<<getGrade(score)<<endl;return0;}

第三部分:参数传递机制(50分钟)

3.1 值传递(Pass by Value)

值传递的特点:

  • 函数接收参数的副本,不是原始变量
  • 在函数内修改参数不会影响原始变量
  • 就像复印文件,修改复印件不影响原件
#include<iostream>usingnamespacestd;// 值传递示例voidmodifyValue(intx){cout<<"函数内修改前: x = "<<x<<endl;x=x*2;// 修改参数的值cout<<"函数内修改后: x = "<<x<<endl;}// 交换两个数的值(错误版本)voidswapWrong(inta,intb){inttemp=a;a=b;b=temp;cout<<"函数内: a = "<<a<<", b = "<<b<<endl;}// 计算矩形面积和周长voidcalculateRectangle(doublelength,doublewidth,double&area,double&perimeter){area=length*width;perimeter=2*(length+width);}intmain(){// 值传递示例intnum=5;cout<<"调用函数前: num = "<<num<<endl;modifyValue(num);cout<<"调用函数后: num = "<<num<<endl;cout<<"注意:num的值没有改变!"<<endl;cout<<"\n--- 交换函数测试 ---"<<endl;intx=10,y=20;cout<<"交换前: x = "<<x<<", y = "<<y<<endl;swapWrong(x,y);cout<<"交换后: x = "<<x<<", y = "<<y<<endl;cout<<"注意:x和y的值没有交换!"<<endl;return0;}

3.2 多返回值问题

#include<iostream>usingnamespacestd;// 方法1:使用多个函数(不推荐)doublecalculateArea(doublelength,doublewidth){returnlength*width;}doublecalculatePerimeter(doublelength,doublewidth){return2*(length+width);}// 方法2:使用引用参数(后续课程详细讲解)voidcalculateRectangle(doublelength,doublewidth,double&area,double&perimeter){area=length*width;perimeter=2*(length+width);}// 方法3:使用数组或结构体(后续课程学习)intmain(){doublelen=5.0,wid=3.0;// 方法1:调用多个函数doublearea1=calculateArea(len,wid);doubleperimeter1=calculatePerimeter(len,wid);cout<<"方法1 - 面积: "<<area1<<", 周长: "<<perimeter1<<endl;// 方法2:使用引用参数doublearea2,perimeter2;calculateRectangle(len,wid,area2,perimeter2);cout<<"方法2 - 面积: "<<area2<<", 周长: "<<perimeter2<<endl;return0;}

第四部分:函数声明和定义分离(30分钟)

4.1 为什么需要函数声明?

问题场景:

#include<iostream>usingnamespacestd;intmain(){// 错误:函数在调用之后定义intresult=multiply(5,3);// 编译错误!cout<<"结果: "<<result<<endl;return0;}// 函数定义在main之后intmultiply(inta,intb){returna*b;}

4.2 函数声明(函数原型)

解决方案:函数声明 + 函数定义

#include<iostream>usingnamespacestd;// 函数声明(函数原型)intmultiply(inta,intb);voidshowInfo(string name,intage);doublecalculateBMI(doubleweight,doubleheight);boolisPrime(intnumber);intmain(){// 现在可以正确调用函数了cout<<"5 × 3 = "<<multiply(5,3)<<endl;showInfo("小明",12);doublebmi=calculateBMI(45.0,1.65);cout<<"BMI: "<<bmi<<endl;intnum=17;if(isPrime(num)){cout<<num<<" 是质数"<<endl;}else{cout<<num<<" 不是质数"<<endl;}return0;}// 函数定义intmultiply(inta,intb){returna*b;}voidshowInfo(string name,intage){cout<<"姓名: "<<name<<", 年龄: "<<age<<endl;}doublecalculateBMI(doubleweight,doubleheight){returnweight/(height*height);}boolisPrime(intnumber){if(number<=1)returnfalse;for(inti=2;i*i<=number;i++){if(number%i==0)returnfalse;}returntrue;}

4.3 多文件组织(了解概念)

// math_functions.h - 头文件(函数声明)#ifndefMATH_FUNCTIONS_H#defineMATH_FUNCTIONS_Hintadd(inta,intb);intmultiply(inta,intb);doubledivide(doublea,doubleb);#endif// math_functions.cpp - 源文件(函数定义)#include"math_functions.h"intadd(inta,intb){returna+b;}intmultiply(inta,intb){returna*b;}doubledivide(doublea,doubleb){if(b!=0)returna/b;elsereturn0;}// main.cpp - 主程序#include<iostream>#include"math_functions.h"usingnamespacestd;intmain(){cout<<"5 + 3 = "<<add(5,3)<<endl;cout<<"5 × 3 = "<<multiply(5,3)<<endl;cout<<"10 ÷ 3 = "<<divide(10,3)<<endl;return0;}

第五部分:递归初步(50分钟)

5.1 什么是递归?

递归的概念:

  • 函数直接或间接调用自身
  • 像俄罗斯套娃,大娃娃里面有小娃娃
  • 像镜子对着镜子,无限反射

生活比喻:

  • 讲故事:“从前有座山,山里有座庙,庙里有个老和尚在讲故事:从前有座山…”
  • 查字典:查一个词,解释中又有不认识的词,继续查…

5.2 递归的基本结构

递归三要素:

  1. 基准情况:递归结束的条件
  2. 递归调用:函数调用自身
  3. 向基准情况推进:每次调用都更接近结束条件

5.3 递归示例:阶乘计算

数学定义:

  • 0! = 1(基准情况)
  • n! = n × (n-1)!(递归关系)
#include<iostream>usingnamespacestd;// 递归函数:计算阶乘intfactorial(intn){// 基准情况if(n==0||n==1){cout<<"到达基准情况: factorial("<<n<<") = 1"<<endl;return1;}// 递归调用cout<<"计算 factorial("<<n<<") = "<<n<<" × factorial("<<n-1<<")"<<endl;intresult=n*factorial(n-1);cout<<"返回 factorial("<<n<<") = "<<result<<endl;returnresult;}intmain(){intnumber=5;cout<<"计算 "<<number<<" 的阶乘:"<<endl;intresult=factorial(number);cout<<number<<"! = "<<result<<endl;return0;}

5.4 递归示例:斐波那契数列

数学定义:

  • F(0) = 0
  • F(1) = 1
  • F(n) = F(n-1) + F(n-2)
#include<iostream>usingnamespacestd;// 递归函数:计算斐波那契数列intfibonacci(intn){// 基准情况if(n==0)return0;if(n==1)return1;// 递归调用returnfibonacci(n-1)+fibonacci(n-2);}// 显示斐波那契数列voidshowFibonacci(intcount){cout<<"斐波那契数列前"<<count<<"项: ";for(inti=0;i<count;i++){cout<<fibonacci(i)<<" ";}cout<<endl;}intmain(){showFibonacci(10);// 计算单个斐波那契数intn=6;cout<<"斐波那契数列第"<<n<<"项: "<<fibonacci(n)<<endl;return0;}

5.5 递归的优缺点

优点:

  • 代码简洁,表达力强
  • 适合解决分治问题
  • 符合数学定义

缺点:

  • 可能效率较低(重复计算)
  • 可能栈溢出(递归深度太大)
  • 可能难以理解

第六部分:综合应用示例(60分钟)

6.1 数学工具包

#include<iostream>#include<cmath>usingnamespacestd;// 函数声明voidshowMenu();intadd(inta,intb);intsubtract(inta,intb);intmultiply(inta,intb);doubledivide(inta,intb);intpower(intbase,intexponent);intfactorial(intn);intgcd(inta,intb);// 最大公约数boolisPrime(intnumber);intmain(){intchoice;do{showMenu();cout<<"请选择操作: ";cin>>choice;if(choice>=1&&choice<=4){inta,b;cout<<"请输入第一个数字: ";cin>>a;cout<<"请输入第二个数字: ";cin>>b;switch(choice){case1:cout<<a<<" + "<<b<<" = "<<add(a,b)<<endl;break;case2:cout<<a<<" - "<<b<<" = "<<subtract(a,b)<<endl;break;case3:cout<<a<<" × "<<b<<" = "<<multiply(a,b)<<endl;break;case4:if(b!=0){cout<<a<<" ÷ "<<b<<" = "<<divide(a,b)<<endl;}else{cout<<"错误:除数不能为0!"<<endl;}break;}}elseif(choice==5){intbase,exp;cout<<"请输入底数: ";cin>>base;cout<<"请输入指数: ";cin>>exp;cout<<base<<"^"<<exp<<" = "<<power(base,exp)<<endl;}elseif(choice==6){intn;cout<<"请输入要计算阶乘的数: ";cin>>n;if(n>=0){cout<<n<<"! = "<<factorial(n)<<endl;}else{cout<<"错误:阶乘只能计算非负整数!"<<endl;}}elseif(choice==7){inta,b;cout<<"请输入第一个数: ";cin>>a;cout<<"请输入第二个数: ";cin>>b;cout<<a<<"和"<<b<<"的最大公约数是: "<<gcd(a,b)<<endl;}elseif(choice==8){intnumber;cout<<"请输入要检查的数: ";cin>>number;if(isPrime(number)){cout<<number<<" 是质数"<<endl;}else{cout<<number<<" 不是质数"<<endl;}}elseif(choice!=0){cout<<"无效选择!"<<endl;}}while(choice!=0);cout<<"感谢使用数学工具包!"<<endl;return0;}// 函数定义voidshowMenu(){cout<<"\n=== 数学工具包 ==="<<endl;cout<<"1. 加法"<<endl;cout<<"2. 减法"<<endl;cout<<"3. 乘法"<<endl;cout<<"4. 除法"<<endl;cout<<"5. 幂运算"<<endl;cout<<"6. 阶乘"<<endl;cout<<"7. 最大公约数"<<endl;cout<<"8. 质数判断"<<endl;cout<<"0. 退出"<<endl;}intadd(inta,intb){returna+b;}intsubtract(inta,intb){returna-b;}intmultiply(inta,intb){returna*b;}doubledivide(inta,intb){returnstatic_cast<double>(a)/b;}intpower(intbase,intexponent){intresult=1;for(inti=0;i<exponent;i++){result*=base;}returnresult;}intfactorial(intn){if(n==0||n==1)return1;returnn*factorial(n-1);}intgcd(inta,intb){if(b==0)returna;returngcd(b,a%b);}boolisPrime(intnumber){if(number<=1)returnfalse;for(inti=2;i*i<=number;i++){if(number%i==0)returnfalse;}returntrue;}

6.2 图形绘制工具

#include<iostream>usingnamespacestd;// 函数声明voiddrawRectangle(intwidth,intheight,charsymbol);voiddrawTriangle(intheight,charsymbol);voiddrawDiamond(intsize,charsymbol);voiddrawChristmasTree(intlevels);intmain(){intchoice;cout<<"=== 图形绘制工具 ==="<<endl;// 绘制矩形cout<<"\n1. 绘制矩形:"<<endl;drawRectangle(8,4,'*');// 绘制三角形cout<<"\n2. 绘制三角形:"<<endl;drawTriangle(5,'#');// 绘制菱形cout<<"\n3. 绘制菱形:"<<endl;drawDiamond(5,'+');// 绘制圣诞树cout<<"\n4. 绘制圣诞树:"<<endl;drawChristmasTree(4);return0;}// 函数定义:绘制矩形voiddrawRectangle(intwidth,intheight,charsymbol){for(inti=0;i<height;i++){for(intj=0;j<width;j++){cout<<symbol;}cout<<endl;}}// 函数定义:绘制三角形voiddrawTriangle(intheight,charsymbol){for(inti=1;i<=height;i++){// 打印空格for(intj=1;j<=height-i;j++){cout<<" ";}// 打印符号for(intj=1;j<=2*i-1;j++){cout<<symbol;}cout<<endl;}}// 函数定义:绘制菱形voiddrawDiamond(intsize,charsymbol){// 上半部分for(inti=1;i<=size;i++){for(intj=1;j<=size-i;j++)cout<<" ";for(intj=1;j<=2*i-1;j++)cout<<symbol;cout<<endl;}// 下半部分for(inti=size-1;i>=1;i--){for(intj=1;j<=size-i;j++)cout<<" ";for(intj=1;j<=2*i-1;j++)cout<<symbol;cout<<endl;}}// 函数定义:绘制圣诞树voiddrawChristmasTree(intlevels){for(intlevel=1;level<=levels;level++){intheight=level+1;for(inti=1;i<=height;i++){// 打印空格for(intj=1;j<=levels-i+1;j++){cout<<" ";}// 打印星号for(intj=1;j<=2*i-1;j++){cout<<"*";}cout<<endl;}}// 树干for(inti=0;i<2;i++){for(intj=1;j<=levels;j++){cout<<" ";}cout<<"|"<<endl;}}

练习与作业

基础练习(必做)

练习1:数学函数库
创建以下数学函数:

  1. square(int n)- 返回n的平方
  2. cube(int n)- 返回n的立方
  3. isEven(int n)- 判断n是否为偶数
  4. isPositive(int n)- 判断n是否为正数
  5. max(int a, int b)- 返回a和b中的较大值

练习2:温度转换器
编写温度转换函数:

  1. celsiusToFahrenheit(double celsius)- 摄氏转华氏
  2. fahrenheitToCelsius(double fahrenheit)- 华氏转摄氏
  3. 编写主程序让用户选择转换方向并输入温度

练习3:字符串处理函数
创建字符串处理函数:

  1. stringLength(string str)- 返回字符串长度(不用内置函数)
  2. countVowels(string str)- 统计元音字母个数
  3. reverseString(string str)- 返回反转后的字符串

挑战练习(选做)

挑战1:递归深度探索

  1. 编写递归函数计算数字的各位数之和
  2. 编写递归函数判断字符串是否是回文
  3. 测试递归深度限制,找出你的计算机最多支持多深的递归

挑战2:函数组合应用
创建一个"数字分析器"程序,包含以下功能:

  1. 判断质数
  2. 计算所有因数
  3. 判断完美数(等于其所有真因数之和的数)
  4. 判断阿姆斯特朗数(各位数字立方和等于自身的数)

挑战3:模块化游戏
将之前学过的猜数字游戏重构为模块化程序:

  • generateRandomNumber()- 生成随机数
  • getUserGuess()- 获取用户猜测
  • giveHint(int guess, int target)- 给出提示
  • playGame()- 游戏主逻辑

实验任务

任务1:函数调用栈实验
通过调试或输出语句跟踪以下递归函数的执行过程:

voidcountDown(intn){if(n<0)return;cout<<"进入: countDown("<<n<<")"<<endl;countDown(n-1);cout<<"离开: countDown("<<n<<")"<<endl;}

任务2:值传递验证
设计实验验证值传递机制:

voidtestValuePassing(intx){x=100;cout<<"函数内: x = "<<x<<endl;}// 调用后检查原始变量是否改变

任务3:函数性能测试
比较递归和迭代版本的阶乘函数:

  • 测试计算20!的时间
  • 测试能计算的最大阶乘值
  • 分析两种方法的优缺点

学习总结

今天学到了:

  • 函数概念:模块化编程的基本单元
  • 函数定义:返回类型、函数名、参数列表、函数体
  • 函数调用:使用函数执行特定任务
  • 参数传递:值传递机制和特点
  • 返回值:函数执行结果的返回
  • 函数声明:提前声明函数原型
  • 递归初步:函数调用自身的编程技巧

关键技能:

  • 模块化设计:将复杂问题分解为函数
  • 接口设计:设计合理的函数参数和返回值
  • 代码复用:通过函数避免重复代码
  • 递归思维:用递归方式解决自相似问题

下一课预告:

下一节课我们将学习排序与查找算法,包括选择排序、冒泡排序、顺序查找和二分查找,这是算法学习的重要基础!


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/6 11:38:27

Windows文件资源管理器美化终极指南:5步实现Mica模糊效果

Windows文件资源管理器美化终极指南&#xff1a;5步实现Mica模糊效果 【免费下载链接】ExplorerBlurMica Add background Blur effect or Acrylic (Mica for win11) effect to explorer for win10 and win11 项目地址: https://gitcode.com/gh_mirrors/ex/ExplorerBlurMica …

作者头像 李华
网站建设 2026/2/8 21:57:56

大数据建模中的模型

在大数据建模中&#xff0c;“模型”一词通常指的是对数据结构、数据关系或数据行为的抽象表示。根据建模目的和应用场景的不同&#xff0c;可以将模型分为多种类型&#xff0c;常见的包括物理模型、概念模型、逻辑模型、理论模型、统计模型、机器学习模型、预测模型、仿真模型…

作者头像 李华
网站建设 2026/2/5 20:18:04

LangGraph入门指南:从零掌握大模型应用的状态管理与流程编排!

简介 文章介绍了LangGraph框架&#xff0c;这是一个专为构建复杂LLM应用设计的低层级编排框架。它通过State(状态)、Node(节点)和Edge(边缘)三个核心组件实现有状态、多步骤、长周期运行的Agent应用。LangGraph提供持久执行、动态控制流和人工介入等特性&#xff0c;支持分支、…

作者头像 李华
网站建设 2026/2/7 13:33:04

C语言中以坐标的方式图解“字母金字塔”的绘制

目录题目题目解析题目理解空格图-坐标解析字母递增图-坐标解析字母递减图-坐标解析代码汇总验证代码汇总终端运行验证坐标图解法的好处建议好处题目 实现字母金字塔&#xff0c;通过键盘输入字符来控制层数&#xff0c;如输入D&#xff0c;则打印下面图形 AABAABCBAABCDCBA题目…

作者头像 李华
网站建设 2026/2/9 16:06:05

Q CLI 助力合合信息实现 Aurora 的升级运营

1. 升级背景 合合信息是一家中国领先的人工智能(AI)产品公司&#xff0c;一直致力于通过AI技术赋能创新&#xff0c;为全球数亿用户和多元化行业提供产品服务。凭借超过18年的AI研究和应用专业知识&#xff0c;合合信息已成为全球多模态大模型文本智能技术的领先者&#xff0c…

作者头像 李华
网站建设 2026/2/2 23:48:37

PDF对比终极指南:三步快速定位文档差异

PDF对比终极指南&#xff1a;三步快速定位文档差异 【免费下载链接】diff-pdf A simple tool for visually comparing two PDF files 项目地址: https://gitcode.com/gh_mirrors/di/diff-pdf 在文档处理工作中&#xff0c;PDF对比是每个职场人士都会遇到的常见需求。无论…

作者头像 李华