c语言基础第12节《函数的调用》

c语言基础10

函数

函数的调用

调用方式

①函数语句:

test(); // 对于无返回值的函数,直接调用
int res = max(2,4); // 对于有返回值的函数,一般需要再主调函数中接收被调函数的返回值。

②函数表达式:

4 + max(2,4)
scanf("%d",&num) != 1

③函数参数

printf("%d",max(2,4)); // 将max(2,4)的值作为了printf()函数的实参

在一个函数中调用另一个函数具备以下条件:

① 被调用的函数必须是已经定义的函数。

② 若使用库函数,应在本文件开头用 #include 包含其对应的头文件。

③ 若使用自定义函数,自定义函数又在主调函数的后面,则应在主调函数中对被调函数进行声明。声明的作用是把函数名、函数参数的个数和类型等信息通知编译系统,以便于在遇到函数时,编译系统能正确识别函数,并检查函数调用的合法性。

函数的声明

函数调用时,往往要遵循先定义后使用,但如果我们对函数的调用操作出现在函数的定义之前,则需要对函数进行声明。

完整的函数使用分为三部分:

  • 函数声明
int max(int x, int y); // 指明参数类型
int max(int,int); // 省略参数类型

函数声明如果是在同一个文件,一定要定义在文件中所有函数定义的最前面。如果有对应的.h文件,可以将函数的声明抽取到.h中。

  • 函数定义
int max(int x, int y) // 函数定义的时候,不能省略参数的数据类型,参数个数,位置要和函数声明时一致
{
return x > y ? x : y;
}
  • 函数调用
int main()
{
printf("%d\n",max(5,3));
return 0;
}

函数声明的作用:

  • 是把函数名函数参数的个数函数参数的类型返回类型等信息通知给编译系统,以便于在遇到函数时,编译系统能正确识别函数,并检查函数调用的合法性(C语言规范)
错误演示:被调函数写在了主调函数之后
// 主调函数
int main()
{
printf("%d\n",add(12,13)); // 此时编译会报编译错误,因为函数没有经过声明,编译系统无法检查函数
的调用的合法性。
}
// 被调函数
int add(int x, int y)
{
return x + y;
}
正确演示:被调函数写在了主调函数之前

注意:如果函数的调用比较简单,并且被调函数写在主调函数之前,此时是可以省略函数声明的。

// 被调函数
int add(int x, int y)
{
return x + y;
}
// 主调函数
int main()
{
printf("%d\n",add(12,13)); // 此时编译通过
}
正确演示:被调函数和主调函数不分前后,需要增加被调函数的函数声明

注意:如果涉及函数的相互嵌套调用,或者复杂嵌套调用,此时是无法区分函数的前后的,这就需要函数声明

// 函数声明
int add(int,int); // 等价于:int add(int x,int y);
// 主调函数
int main()
{
// 函数调用
printf("%d\n",add(12,13)); // 此时编译通过
}
// 被调函数(函数定义)
int add(int x, int y)
{return x + y;
}

声明的方式:

  • 函数首部加上分号
void fun(int a,float b);
  • 函数首部加上分号,可省略形参名,但不能省略参数类型
void fun(int,float);

函数的嵌套调用

  • 函数不允许嵌套定义,但允许嵌套调用

    • 正确:函数嵌套调用
    void a(){..}
    void b()
    {a();
    }
    
    • 错误:函数嵌套定义
    void a()
    {void b(){}
    }
    
  • 嵌套调用:在被调函数内又主动去调用其他函数,这样的函数调用形式,称之为嵌套调用。

funa(){..}
funb(){funa();}
main(){funb();}
案例

案例1:

  • 编写一个函数,判断给定的3~100的正整数是否是素数,若是返回1,否则返回0
  • 代码:
/*************************************************************************
> File Name: demo01.c
> Author: FPF
> Description: 函数案例
> Created Time: 2025年02月20日 星期四 11时44分56秒
************************************************************************/
#include <stdio.h>
/**
* 定义一个函数,求素数
* @param n:判断这个数是否是素数
*/
int sushu(int n)
{
int k,i,flag = 1;
// 素数:只能被1和自身整除的数,所以我们需要校验的是这个数n是否能被 2~n-1之间的数整除,一旦
出现一次能整除的情况,这个数n就不是素数
for(i = 2; i <= n -1; i++)
{
if(n % i == 0)
{
flag = 0;
break;
}
}
return flag;
}
int main(int argc,char *argv[]){
for(int i = 3; i <= 100; i++)
{
if(sushu(i)==1)
{
printf("%-4d",i);
}
}
printf("\n");
return 0;
}

案例1:

  • 输入四个整数,找出其中最大的数,用函数嵌套来实现,要求每次只能两个数比较
  • 代码:
/*************************************************************************
> File Name: demo01.c
> Author: 
> Description: 需求:函数案例-输入四个整数,找出其中最大的数,用函数嵌套来实现,要求每次只能两
个数比较
> Created Time: 2024年12月06日 星期五 09时29分50秒
************************************************************************/
#include <stdio.h>
// 函数声明
int max_2(int,int);
int max_4(int,int,int,int);
/**
* 2个数求最大值
*/
int max_2(int a,int b)
{
return a > b ? a : b;
}
/**
* 4个数求最大值
*/
int max_4(int a,int b,int c,int d)
{
// 写法1
// int max; // 存储最大值
// max = max_2(a,b); // 求a,b的最大值
// max = max_2(max,c); // 求a,b,c的最大值
// max = max_2(max,d); // 求a,b,c,d的最大值
// return max;
// 写法2
return max_2(a,b) > max_2(c,d) ? max_2(a,b) : max_2(c,d);
}
int main(int argc,char *argv[])
{
int a=12,b=44,c=33,d=16,result;
result = max_4(a,b,c,d);
printf("%d,%d,%d,%d中的最大值是%d\n",a,b,c,d,result);
return 0;
}

函数的递归调用

  • 递归调用的含义:在一个函数中,直接或者间接调用了函数自身,就称之为函数的递归调用。
// 直接调用
a()a();
// 间接调用
a()b()a();
a()b()..a();
  • 递归调用的本质:

    是一种循环结构,他不同于我们之前所学的while,do…while,for这样的循环结构,这些循环结构是借助于循环变量;而递归调用是利用函数自身实现循环结构,如果不加以控制,很容易产生死循环。

  • 递归调用的注意事项:

    1. 递归调用必须要有出口,一定要终止递归(否则就会产生死循环)。
    2. 对终止条件的判断一定要放在函数递归之前(先判断,再执行)
    3. 进行函数的递归调用。
    4. 函数递归的同时一定要将函数调用向出口逼近。
案例

案例1:

  • 递归案例-有5个人坐在一起,

    问第5个人多少岁?他说比第4个人大2岁。

    问第4个人岁数,他说比第3个人大2岁。

    问第3个人,又说比第2个人大2岁。

    问第2个人,说比第1个人大2岁。

    最后问第1个人,他说是10岁。请问第5个人多大。

  • 代码:

/*************************************************************************
> File Name: demo02.c
> Author: FPF
> Description: 递归案例
> Created Time: 2025年02月20日 星期四 14时21分51秒
************************************************************************/
#include <stdio.h>
/**
* 求年龄的递归函数
* @param n 第几个人
*/
int age(int n)
{
// 创建一个变量,存储函数返回值,返回的是每个人的年龄
int c;
// 第1个人的年龄是已知的,10岁
if(n == 1)
{
c = 10;
}
else if (n > 1) // 只过滤正数,也就是非第1人
{
c = age(n-1) + 2; // 当前这个人的年龄 = 上一个人的年龄 + 2
}
return c;
}
int main(int argc,char *argv[])
{
printf("%d\n",age(5));
return 0;
}

案例2:

  • 求阶乘(n!)
  • 代码:
/*************************************************************************
> File Name: demo03.c
> Author: FPF
> Description: 递归案例:求阶乘(5的阶乘:5×4×3×2×1)
> Created Time: 2025年02月20日 星期四 14时43分45秒
************************************************************************/
#include <stdio.h>
/**
* 定义一个函数,用来求n的阶乘
* @param n 上限
*/
long fac(int n)
{
// 定义一个变量,用做这个函数的返回值,也就是乘积
long f;
// 出口校验
if(n < 0)
{
printf("n的范围不能是0以下的数\n");
return -1;
}
else if(n == 0 || n == 1)
{
f = 1; // 出口
}
else // 递归
{
f = fac(n-1) * n;}
return f;
}
int main(int argc,char *argv[])
{
int n;
printf("请输入一个整数:\n");
scanf("%d",&n);
printf("%d的阶乘结果是%ld\n",n,fac(n));
return 0;
}

数组做函数参数

​ 当用数组做函数的实参时,则形参应该也要用数组/指针变量来接收(函数实参是数组,形参一定是数组或者指针),注意的是,此次传递并不代表传递了数组中所有的元素数据,而是传递了第一个元素的内存地址(数组首地址),形参接收到这个地址后,则形参和实参就代表了同一个块内存空间,则形参的数据修改会改变实参的。这种数据传递的方式,我们称之为地址传递。

在这里插入图片描述

​ 如果用数组作为函数的形参,那么我们提供另一个形参表示数组的元素个数。原因是数组形参代表的仅仅是实际数组的首地址。也就是说形参只获取到了实参数组的第一个元素的地址,并不确定传递了多少个数据。所以提供另一个形参表示数组元素的容量,可以防止在被调函数对实际数组访问时产生的下标越界。举例:

// 定义一个函数,将数组作为形参
void fun(int arr[], int len) // 数组传参,形参只能接收到实参数组的首地址,并不是完整的数组
{
// int l = sizeof(arr)/ sizeof(arr[0]); 此时编译报错,因为无法确定arr的实际大小,所以无法通过
sizeof进行计算
for(int i = 0; i < len; i++)// len就是传递过来的实参数组的大小
{
printf("%-4d",arr[i]);
}
printf("\n");
}
void main()
{
int arr[] = {11,22,33,44,55};
int len = sizeof(arr) / sizeof(arr[0]);
fun(arr,len);
}

​ 但有一个例外,如果是用字符数组做形参,且实参数组中存放的是字符串数据(形参是字符数组,实参是字符串常量)。则不用表示数组元素的个数的形参,原因是字符串本身会自动结束\n ,举例:

// 定义一个函数,将数组作为形参
void fun(char arr[100]) // 数组传参,形参只能接收到实参数组的首地址,并不是完整的数组
{
char c;
int i = 0;
while((c =arr[i])!='\0')
{
printf("%c",c);
i++;
}
printf("\n");
}
void main()
{
fun("hello");
}
案例

案例1:

有两个数组a和b,各有5个元素,将它们对应元素逐个地相比(即a[0]与b[0]比,a[1]

与b[1]比……)。如果a数组中的元素大于b数组中的相应元素的数目多于b数组中元素大

于a数组中相应元素的数目(例如,a[i]>b]i]6次,b[i]>a[i] 3次,其中i每次为不同的值),则

认为a数组大于b数组,并分别统计出两个数组相应元素大于、等于、小于的个数。

int a[10] = {12,12,10,18,5};
int b[10] = {111,112,110,8,5};
  • 案例:
/*************************************************************************
> File Name: demo04.c
> Author: FPF
> Description: 数组作为函数参数
> Created Time: 2025年02月20日 星期四 16时09分05秒
************************************************************************/
#include <stdio.h>
#define LEN 5
/**
* 定义一个函数,实现两个数的比较
* @param x,y 参与比较的两个数字
* @return 比较结果,x > y 返回1; x < y 返回 -1; x == y 返回 0
*/
int large(int x, int y)
{
int flag = 0;
if (x > y) flag = 1;
else if(x < y) flag = -1;
return flag;
}
int main(int argc,char *argv[])
{
// 定义两个数组,循环变量,最大,最小,相等int a[LEN],b[LEN],i,max=0,min=0,k=0;
printf("请给数组a添加5个整数:\n");
for(i = 0; i < sizeof(a)/sizeof(a[0]); i++)
{
scanf("%d",&a[i]);
}
printf("\n");
printf("请给数组b添加5个整数:\n");
for(i = 0; i < sizeof(b)/sizeof(b[0]); i++)
{
scanf("%d",&b[i]);
}
printf("\n");
// 遍历
for(i = 0; i < LEN; i++)
{
// 比较两个数组中,同一位置两个元素的大小
int value = large(a[i],b[i]);
if(value == 1) max++; // 记录a比b大的次数
else if(value == -1) min++; // 记录a比b小的次数
else k++; // 记录a和b相等的次数
}
printf("max=%d,min=%d,k=%d\n",max,min,k);
return 0;
}

案例2:

  • 需求:编写一个函数,用来分别求数组score_1(有5个元素)和数组score_2(有10个元素)各元素的平均值 。
  • 代码:
/*************************************************************************
> File Name: demo05.c
> Author: FPF
> Description: 数组作为函数参数
> Created Time: 2025年02月20日 星期四 16时28分25秒
************************************************************************/#include <stdio.h>
/**
* 定义一个函数,用来求数组中个元素的平均值
*/
float avg(float scores[],int len)
{
int i;
float aver,sum = scores[0];// 保存平均值和总分
// 遍历数组
for(i = 1; i < len; i++)
{
sum += scores[i];
}
// 求平均分
aver = sum / len;
return aver;
// return sum / len; 等价于上面写法
}
int main(int argc,char *argv[])
{
// 准备俩测试数组
float scores1[] = {66,78,86,56,46};
float scores2[] = {77,88,67,78,98,32,78,88,77,39};
printf("这个班的平均分:%6.2f\n",avg(scores1,sizeof(scores1)/sizeof(float)));
printf("这个班的平均分:%6.2f\n",avg(scores2,sizeof(scores2)/sizeof(float)));
return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/21285.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++:iterator迭代器失效

说明&#xff1a;这里拿 vector 举例。 原因一&#xff1a;扩容导致迭代器失效 // 迭代器失效 void insert(iterator pos, const T& x) {assert(pos > _start);assert(pos < _finish);// 满了就扩容if (_finish _end_of_storage){reserve(capacity() 0 ? 4 : ca…

QT之改变鼠标样式

QT改变鼠标图片 资源路径如下 代码实现 QPixmap customCursorPixmap(":/images/mouse.png");QCursor customCursor(customCursorPixmap);QWidget::setCursor(customCursor); // 可以设置为整个窗口或特定控件QWidget::setCursor(); // 设置为透明光标&#xff0c…

用DeepSeek零基础预测《哪吒之魔童闹海》票房——从数据爬取到模型实战

系列文章目录 1.元件基础 2.电路设计 3.PCB设计 4.元件焊接 5.板子调试 6.程序设计 7.算法学习 8.编写exe 9.检测标准 10.项目举例 11.职业规划 文章目录 **一、为什么要预测票房&#xff1f;****二、准备工作****三、实战步骤详解****Step 1&#xff1a;数据爬取与清洗&am…

高并发下秒杀系统的设计

文章目录 1 业界通用做法1.1 压力分摊1.2 RedisMySQL1.3 Inventory Hint1.4 压力分摊RedisMQ 2 Redis MQ 解决高并发下的秒杀场景2.1 Redis库存预扣减2.1.1 lua脚本执行流程&#xff1a;2.1.2 Lua脚本主要做了几件事&#xff1a; 2.2 MySQL库存扣减2.3 记录操作流水的原因 3 I…

双重差分学习笔记

双重差分适用的研究场景&#xff1a; 研究某项政策或者冲击造成的影响 例如&#xff0c;某某小学在2024.12.12日颁布了小红花激励措施&#xff0c;我们要研究这项措施对学生成绩的影响&#xff0c;此时&#xff0c;就可以使用双重差分模型。 双重差分适用的数据类型&#xf…

深入理解 C++17 中的 std::atomic<T>::is_always_lock_free

文章目录 原子操作与锁无关性&#xff08;Lock-Free&#xff09;锁无关性&#xff08;Lock-Free&#xff09;无锁&#xff08;Lock-Free&#xff09;与无阻塞&#xff08;Wait-Free&#xff09; std::atomic<T>::is_always_lock_free 是什么&#xff1f;truefalse与 is_l…

VSCode 中 Git 添加了多个远端,如何设置默认远端

VSCode 中 Git 添加了多个远端&#xff0c;如何设置默认远端 查看分支&#xff1a;设置默认远端手动指定远端 查看分支&#xff1a; * 表示当前默认远端 git branch -vv* master a1b2c3d [origin/main] Fix typo dev d4e5f6g [upstream/dev] Add feature设置默认远端 将本…

一文讲清 AIO BIO NIO的区别

引言 在 Java 编程中&#xff0c;BIO&#xff08;Blocking I/O&#xff09;、NIO&#xff08;Non-blocking I/O&#xff09;和 AIO&#xff08;Asynchronous I/O&#xff09;是三种不同的 I/O 模型&#xff0c;它们在处理输入输出操作时有着不同的机制和特点&#xff0c;但是市…

使用(xshell+xftp)将前端项目部署到服务器

一.以vue项目为例 将项目打包生成dist文件 二.下载载安装xshell和xftp 下载地址&#xff1a;家庭/学校免费 - NetSarang Website 三.连接服务器 在xshell新建会话&#xff08;需要用到服务器、用户名、密码、端口号&#xff09;正确输入后连接到服务器 使用命令连接&#x…

硬件岗位是否适合你?

在当今科技飞速发展的时代,硬件行业作为技术创新的基石,始终扮演着至关重要的角色。无论是智能手机、自动驾驶汽车,还是人工智能服务器,硬件都是这些技术的核心支撑。然而,硬件岗位是否适合你?作为一名硬件专家,我将从多个角度为你分析,帮助你判断自己是否适合从事硬件…

Linux基本指令(二)

文章目录 基本指令echocat&#xff08;输入重定向&#xff09;history日志moretail和headmv&#xff08;重要&#xff09;时间相关的指令查找的命令 知识点Linux下一切皆文件为什么计算机关机了&#xff0c;开机后还能准确地记录时间呢&#xff1f; 基本指令 echo 1. echo&…

【Blender】二、建模篇--05,阵列修改器与晶格形变

阵列修改器是bender里面一个比较常用的修改器,所以我们单独开口来讲,我们会先从几片树叶出发,然后我们用阵列修改器把这几片树叶变成这样的造型和这样的造型。这两个造型分别就代表着阵列修改器最常用的两种偏移方法,我们现在就开始我们先来做几个树叶。 1.树叶建模 首先…

fpga助教面试题

第一题 module sfp_pwm( input wire clk, //clk is 200M input wire rst_n, input wire clk_10M_i, input wire PPS_i, output reg pwm ) reg [6:0] cunt ;always (posedge clk ) beginif(!rst_n)cunt<0;else if(cunt19) //200M是10M的20倍cunt<0;elsecunt<cunt1;…

SpringAI系列 - ToolCalling篇(二) - 如何设置应用侧工具参数ToolContext(有坑)

目录 一、引言二、集成ToolContext示例步骤1: 在`@Tool`标注的工具方法中集成`ToolConext`参数步骤2:`ChatClient`运行时动态设置`ToolContext`参数三、填坑一、引言 在使用AI大模型的工具调用机制时,工具参数都是由大模型解析用户输入上下文获取的,由大模型提供参数给本地…

Jest单元测试

由于格式和图片解析问题&#xff0c;可前往 阅读原文 前端自动化测试在提高代码质量、减少错误、提高团队协作和加速交付流程方面发挥着重要作用。它是现代软件开发中不可或缺的一部分&#xff0c;可以帮助开发团队构建可靠、高质量的应用程序 单元测试&#xff08;Unit Testi…

pyside6学习专栏(二):程序图像资源的加载方式

pyside6中的QLabel控件可以加载图像和gif动画&#xff0c;可以直接从外部文件加载&#xff0c;也可以从QRC类型的文件(实际是一脚本文件)经编绎生成对应的资源.PY模块文件(就是将qrc文本中指定的资源文件的16制内容写入.py文件)来使用&#xff0c;本文对两种方式作了一简单的示…

Nginx--日志(介绍、配置、日志轮转)

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、Nginx日志介绍 nginx 有一个非常灵活的日志记录模式&#xff0c;每个级别的配置可以有各自独立的访问日志, 所需日志模块 ngx_http_log_module 的…

cs106x-lecture12(Autumn 2017)-SPL实现

打卡cs106x(Autumn 2017)-lecture12 (以下皆使用SPL实现&#xff0c;非STL库&#xff0c;后续课程结束会使用STL实现) travel Write a recursive function named travel that accepts integers x and y as parameters and uses recursive backtracking to print all solution…

了解随机振动疲劳分析中 Ansys nCode DesignLife 的平均应力校正

概括 在本篇博文中&#xff0c;我们将探讨 Ansys nCode 在分析随机振动引起的疲劳方面的重要性。我们将了解 nCode 如何帮助校正平均应力并预测受随机振动影响的结构的寿命和耐久性。 什么是疲劳寿命以及了解平均应力对疲劳寿命的影响 疲劳寿命是指结构在重复载荷作用下发生…

ubuntu20.04重启后不显示共享文件夹

ubuntu20.04重启后不显示共享文件夹 主要参见这两篇博客 Ubuntu重启后不显示共享文件夹_ubuntu 20.04 共享目录无法使用-CSDN博客 ubuntu22.04 配置共享文件夹 找不到/mnt/hgfs_ubuntu安装tools 后mnt文件夹在哪-CSDN博客 重启Ubuntu20.04后&#xff0c;发现共享文件夹进不去…