C语言基础——函数(2)

             ʕ • ᴥ • ʔ             

 🎉 🎉

文章目录

前言

一、return语句

二、数组做函数参数

三、嵌套调用和链式访问

3.1 嵌套调用

3.2 链式访问

四、函数声明和定义

4.1 单个文件

4.2 多个文件

总结


前言

大家好啊,继我们上一次讲的函数(1)已经过了两周了,不知道大家掌握的怎么样,由于要期末考试,我断更了两周,希望大家见谅,那么我们接着上一章节来继续说说函数的内容吧。我们这一章节就来看看return语句和数组传参及嵌套调用和链式访问等问题,接下来我们来一起学习本次的内容。


一、return语句

我们在使用函数的过程中,总是使用到return语句,例如

int Add(int a, int b)
{int c = a + b;return c;
}

既然return这么重要,那它到底怎么使用呢,我们来了解了解。

1. return后面可以是一个数值,也可以是一个表达式,但是如果return后面是表达式的话要先计算表达式的内容,在返回结果。

2. return后面可以什么都没有,直接写return,这种写法适合函数返回类型是void(无类型返回)的情况

3. return返回的值和函数返回的类型不一致,系统会自动返回的值转换成函数返回的类型。

4. return执行后,函数就彻底返回,后面的代码不执行。

5. 如果函数中出现了if分支,要确保每个分支都有返回的情况,不然程序会报错。

我来一条一条的为大家解释。

首先是第一条,我们可以从上节课的代码就能知道,return可以直接返回数值,也可以返回一个表达式。

int Add(int a, int b)
{int c = a + b;return c;
}
int Add(int a, int b)
{return a + b;
}

表达式是先通过计算出结果后才返回最终的值的,这很好理解,就不多赘述。

其次是第2条也很好理解,函数本身是不需要任何的返回类型的,那return后面肯定什么也不能加的,可能会有人疑问那这个return加在这个函数里面有什么用,当然是有用的这个就可以和第4点结合起来,它可以直接中断函数而不继续运行下面的代码。这就是这个return的作用。

void Print(int n)
{if (n <= 0){return;}for (int i = 1; i <= n; i++){printf("%d ", i);}
}int main()
{int n = 0;scanf("%d", &n);Print(n);return 0;
}

我们可以来看这个代码,这个代码的目的是如果n>0就把1~n的数字打印出来,我们可以来看这个打印的函数Print就可以看到,如果n<=0就return,我们可以来看看这个return后还会不会继续运行函数下面的代码了。

可以看到,我输入一个正数它很好的打印出来了,那我输入一个负数或者0会怎么样呢?

很显然,它什么都没有输出,这就是return的强大,它比break都强,break是跳出循环,而return是直接跳出函数。

我们现在来看看第3点,其实也很好理解,就是如果return返回的类型和函数要求的返回类型不一样是会转换成函数类型。

char test(char a)
{int b = a;return b;
}int main()
{char a;scanf("%c", &a);int b = sizeof(test(a));printf("%d", b);return 0;
}

我们可以来看看这个代码,我们先输入一个字符,然后写一个函数,这个函数的返回类型是字符型,但是我却返回了一个整型,那我们来看看它到底返回的是字符还是整型吧,我们可以知道字符的sizeof是1,而整型是大于等于2。

可以看到是1,所以返回回来的是字符型。也就是函数要求的返回类型。

最后我们来看看第5点,其实也很简单,你想想,如果一个要求要返回内容的函数却没有返回内容会怎么办,肯定会出现问题啊,可能你十分肯定不会有其他情况发生,但是万一呢,万一发生了呢,可能你会想那再改嘛,但是给了用户后发生这种情况导致程序崩溃了,那是多大的损失啊。所以为了防止这种事情的发生,函数中如果有if,那必须都有返回值,不然程序会报错的。

二、数组做函数参数

我们在使用函数的过程之中,难免会使用到数组做参数,就比如说我们如果想写一个函数,让它把数组里的数据都变成0,那就得把数组传参到函数中,那我们接下来了解了解数组如何传参吧。

我们可以先来写两个关于数组传参的函数,第一个就是把数组里的数据都变成0的函数,第二个则是打印数组的函数。

int main()
{int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };set_arr();     //把数组里的数据都变成0print_arr();   //打印数组的函数return 0;
}

我们可以先来想想,如果我们要去让数组内的数据都变成0的话,肯定还是要知道数组里的元素个数的,而且我们之前传实参的时候都是在()内输入要传过去的东西的名字的,按道理来讲数组也是这样的,我们之前说过数组的名是[ ]前的,也就是arr,所以说最后就变成了这样。

int main()
{int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int sz = sizeof(arr) / sizeof(arr[0]); //求元素的个数set_arr(arr, sz);     //把数组里的数据都变成0print_arr(arr, sz);   //打印数组的函数return 0;
}

这就是实参的写法,实参传输过去变成形参啦,那形参该怎么写呢?我们参考上一章节的内容就可以知道其实很简单,唯一困难的地方就是不知道数组该用什么类型来表示。这里我们来复习一下我们前面讲数组是说数组是什么类型的啊,除去我们命名的东东其他的就是它的类型,比如我们这里的arr它的类型是int [ ],那我们的形参是不是 int [ ] arr呢,答案肯定是否定的,其实没有那么复杂,直接就是数组啥样它啥样就行了,数组是int arr[ ],那形参也是int arr[ ];当然,如果你想换个名字,比如说int brr[ ]也可以。所以说最终函数长这样。

void set_arr(int arr[], int sz)
{for (int i = 0; i < sz; i++){arr[i] = 0;}
}void print_arr(int arr[], int sz)
{for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}
}int main()
{int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };int sz = sizeof(arr) / sizeof(arr[0]); //求元素的个数set_arr(arr, sz);     //把数组里的数据都变成0print_arr(arr, sz);   //打印数组的函数return 0;
}

这就是数组的传参方式,当然,不知这一种,等我们学到指针时还有其他的办法会更实用一点。当然,数组这里还有几个重要的知识点得掌握。

1. 函数的形式参数要和函数的实际参数相匹配。

也就是说实参传了两个参数过去,那形参就一定要用两个参数接收,不能多也不能少。

2. 函数的实参是数组,形参也是可以写成数组的形式的。

3. 形参如果是一维数组,那数组的大小可以省略不写。

也就和我上面的相同,[ ]中没有数组的大小,当然如果写了也无伤大雅。

4. 形参如果是二维数组,行可以省略,但是列不行,就和创建二维数组相同。

5. 数组传参,形参是不会创建新数组的。

也就是说实参的数组和形参的数组是一个数组,而不是和上节课那样只是数据相同,但不是同一个东西,数组就是同一个数组。

6. 形参操作的数组和实参操作的数组是同一个数组。

同上面同理。

三、嵌套调用和链式访问

3.1 嵌套调用

嵌套调用就是函数之间相互调用,就像孔明锁或者鲁班锁,各种不同的结构相互平凑在一起,形成了一个非常稳固的结构。而正是因为函数之间有效的相互调用才形成了相对的的程序。

假如我们要写一个程序,要让它求出某年某月有多少天怎么做呢?我们来看一下。

int main()
{int years = 0;int month = 0;scanf("%d %d", &years, &month);int day = get_month_day(years, month); // 用来求某年某月有多少天的函数printf("%d", day);return 0;
}

这是主函数,我们可以先创建一个函数来解决这个问题,我们来分析一下这个问题,其实很简单,月份基本上是固定的,唯一一个不固定的月份就是二月,闰年二月29天,平年二月28天,所以问题就变成了一个找闰年的问题,我们可以再创建一个函数来判断是不是闰年。

int is_leap_year(int y) //是闰年返回1,不是返回0
{if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0){return 1;}else{return 0;}
}
int get_month_day(int y, int m)
{int arr[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };//由于arr的下标是从0开始的,所以说我们可以空一位,让下标0变成0if (is_leap_year(y) == 1 && m == 2){return arr[m] + 1;}else{return arr[m];}
}int main()
{int years = 0;int month = 0;scanf("%d %d", &years, &month);int day = get_month_day(years, month); // 用来求某年某月有多少天的函数printf("%d", day);return 0;
}

我们可以得到以上的函数,这就能很好的满足我们的要求,这就是嵌套调用。

当然还可以用switch,大家可以去试一试,这里就不带大家尝试了。

大家要注意函数不能嵌套定义,就是说在一个函数中在定义一个函数是万分不可的。

3.2 链式访问

链式访问就是值把一个函数的返回值作为一个函数的参数,像链条一样将函数串起来就是链式访问。

就比如我们之前玩过的strlen函数,它的作用是求字符串长度

int main()
{int a = strlen("ABCDEFG");printf("%d\n", a);return 0;
}

这个代码很容易懂,就是求出ABCDEFG的大小后存储在a中,然后再用printf函数输出出来。

可能有人就在想了,能不能直接把strlen写在printf里面,不用a保存,比如

int main()
{printf("%d\n", strlen("ABCDEFG"));return 0;
}

当然可以,这就是一个链式访问,将strlen函数的返回值放在printf的参数之中。

四、函数声明和定义

4.1 单个文件

我们在创建函数的时候一般都是把函数写在主程序的前面,例如

int Add(int a, int b) //函数的定义
{return a + b;
}int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);int c = Add(a, b);//函数的调用printf("%d", c);return 0;
}

我们把上面的函数叫做函数的定义,而下面引用函数的地方叫做函数的调用。但是我们其实也可以把函数写在主函数的下面。

int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);int c = Add(a, b);//函数的调用printf("%d", c);return 0;
}int Add(int a, int b) //函数的定义
{return a + b;
}

这个时候我们运行代码会发现程序报错了。

上面显示说是Add未定义,原因是因为编译器在从上往下编译时发现主程序中有Add这个函数,但是它在前面没有看见过,所以就显示未定义,这个时候我们就可以声明一下。

int Add(int a, int b);//函数的声明int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);int c = Add(a, b);//函数的调用printf("%d", c);return 0;
}int Add(int a, int b) //函数的定义
{return a + b;
}

这就是函数的声明,这样程序就能正常运行了,当然,函数的定义是特殊的函数声明。

4.2 多个文件

其实我们一般在创建函数是都不会在一个文件中创建,一般会分为两个文件

.h(头文件) ---- 函数的声明;

.c(源文件) ---- 函数的定义;

以上就是我们声明函数的地方。

以上就是我们函数定义的地方。

我们在头文件中声明函数,在源文件中定义函数,最后再在我们的主文件中使用函数,这就是多个文件的函数声明和定义。

声明:

定义:

使用:

在使用时要注意要引用我们自己创建的头文件,我们自己的头文件用" "来引用。


总结

以上就是函数的内容啦,希望大家好好吸收和理解下一章节我来说说操作符吧。感谢大家的观看,如果哪里有误,欢迎支持,谢谢大家。

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

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

相关文章

优化系统小工具

一款利用VB6编写的系统优化小工具&#xff0c;系统优化、桌面优化、清理垃圾、查找文件等功能。 下载:https://download.csdn.net/download/ty5858/89432367

构造,析构,垃圾回收

构造函数 基本概念 在实例化对象时 会调用的用于初始化的函数 如果不写&#xff0c;默认存在一个无参构造函数 构造函数的写法 1.没有返回值 2.函数名和类名必须相同 3.没有特殊需求时&#xff0c;一般都是public的 4.构造函数可以被重载 5.this代表当前调用该函数的对…

前端JS计算精度损失的问题

例子: 0.10.2 //0.30000000000000004 1.2-1 //0.19999999999999996 1.15*100 //114.99999999999999 1.2/0.2 //5.999999999999999 方式1 // 两个浮点数求和 function num_add(num1,num2){var r1,r2,m;try{r1 num1.toString().split(.)[1].length;}catch(e){r1 0;}try{r2num…

【UE5.3】笔记1

内容浏览器&#xff1a;存放项目中所有的资源&#xff1a;关卡、蓝图类...... 关卡--Map 至少有一个关卡&#xff0c;可以有多个关卡 -漫游 视野漫游&#xff1a;鼠标右键WASD QE 鼠标滑轮控制摄像机速度 运行&#xff0c;ESC退出运行,快捷键F8不停止运行单独弹出功能 -创…

计算机视觉全系列实战教程 (十三):图像形态学操作

1.基本概述 (1)What 图像的形态学操作的本质&#xff1a;集合间的运算 几何学 (2)Why(有什么用途&#xff09; 消除噪声、边缘提取、区域填充、细化和粗化、分割独立的图像元素、求图像梯度、求极大值区域或极小值区域等。 (3)Which(有哪些常见的形态学操作) A.膨胀 使得…

Express的模块化路由

Express的模块化路由是一种组织和管理路由的有效方式&#xff0c;它使得代码更加清晰、易于维护和扩展。 1. 模块化路由的概念 模块化&#xff1a;在编程中&#xff0c;模块化是将程序拆分成多个独立但相互依赖的模块&#xff0c;每个模块都包含特定的功能和数据。Express模块…

使用了CDN,局部访问慢,如何排查

如果是局部访问慢&#xff0c;则可从如下角度查看 是否DNS设置错误导致&#xff1f; 个别用户可能存在local DNS设置错误&#xff0c;导致出现跨地域或跨运营商访问。因为CDN的权威DNS是基于用户请求的localDNS来判断所属的地区和运营商&#xff0c;从而将请求引导至对应最近…

《编译原理》阅读笔记:p19-p24

《编译原理》学习第 4 天&#xff0c;p19-p24总结&#xff0c;总计 5 页。 一、技术总结 1.grouping of phases 这里谈到分组(group)&#xff0c;那么就会有一个疑问&#xff0c;分组的依据是什么&#xff1f;即根据什么来分组。 (1) front end & back end 编译器包含…

找不到d3dcompiler_47.dll如何修复,这几种修复方法可搞定

最近&#xff0c;我在尝试运行一款游戏时遇到了一个问题&#xff0c;系统提示我丢失了d3dcompiler_47.dll文件。这让我感到非常困扰&#xff0c;因为这个问题导致我无法正常运行游戏。经过一番搜索和尝试&#xff0c;我找到了几种修复这个问题的方法&#xff0c;并成功解决了这…

【内网穿透】FRP 跨平台内网穿透 支持windows linux x86_64 arm64 端口范围映射

AI提供的资料&#xff1a; FRP&#xff08;Fast Reverse Proxy&#xff09;是一个专为内网穿透设计的高性能反向代理程序。以下是一些关于FRP的详细资料&#xff0c;帮助您更好地理解和使用这一工具&#xff1a; 核心特点&#xff1a; 内网穿透&#xff1a;能够将位于内网的…

都2024年了,现在互联网行情怎样?

都2024年了&#xff0c;互联网行情是怎样的&#xff1f; 很直白的说&#xff0c;依旧是差得很&#xff0c;怎么说&#xff1f; 我刚在掘金上看到一个掘友写的文章&#xff0c;他是四月领了大礼包&#xff0c;据他的描述如下&#xff1a; 互联网行情依旧是差得很&#xff0c;很…

搜维尔科技:「研讨会」惯性动捕技术在工效学领域应用研讨会

Movella将于7月2日&#xff08;周二&#xff09;下午2点举行主题为惯性动捕技术在工效学领域应用的研讨会。来自Movella的伙伴赋能经理Jeffrey Muller作为嘉宾出席&#xff0c;届时主讲人将为大家带来Xsens惯性动捕技术在工效学领域的应用分享。同时&#xff0c;研讨会还邀请多…

代码随想三刷贪心篇1

代码随想三刷贪心篇1 455. 分发饼干题目代码376. 摆动序列题目代码53. 最大子数组和题目代码455. 分发饼干 题目 链接 代码 class Solution {public int findContentChildren(int[] g, int[] s) {Arrays

监控https证书的到期时间

实现该功能&#xff0c;不用借助第三方库&#xff0c;用go的标准库就足够了… 以下程序可以获取这些域名的SSL证书的到期时间&#xff0c;并在证书距离现在不足7天过期时打印提示&#xff1a; package mainimport ("crypto/tls""fmt""net""…

计算机网络基础入门

计算机网络基础入门 目录&#xff1a; 简介网络分层模型数据封装与解封装IP地址与子网掩码网络协议示例代码 1. 简介 计算机网络是指将地理位置不同的多台计算机及外部设备通过通信线路连接起来&#xff0c;实现信息资源共享和信息传递的系统。计算机网络是现代信息社会的基…

运维.云技术学习.基于应用服务网格的灰度发布(上:理论基础篇)

运维专题 基于应用服务网格的灰度发布&#xff08;上&#xff1a;理论基础篇&#xff09; - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAdd…

Opencv学习项目6——pyzbar

在之前我们学习了解码图片中的二维码&#xff0c;这次我们开启摄像头来解码视频中二维码 开启摄像头 # 打开摄像头 cap cv2.VideoCapture(0) cap.set(3, 640) # 设置摄像头画面宽度 cap.set(4, 480) # 设置摄像头画面高度 我使用的是笔记本上的摄像头来进行的&#xff0c;…

openGauss学习笔记-304 openGauss AI特性-AI4DB数据库自治运维-ABO优化器-智能基数估计

文章目录 openGauss学习笔记-304 openGauss AI特性-AI4DB数据库自治运维-ABO优化器-智能基数估计304.1 概述304.2 前置条件304.3 使用指导304.4 最佳实践304.5 常见问题处理openGauss学习笔记-304 openGauss AI特性-AI4DB数据库自治运维-ABO优化器-智能基数估计 304.1 概述 智…

通过systemctl启停tomcat

目录 目的.service配置文件的结构介绍实验步骤1. 安装java2. 二进制安装tomcat3. 编写/usr/systemd/system/tomcat.service文件4. 测试启动关闭 目的 通过二进制安装的tomcat&#xff0c;只能通过tomcat文件目录下的.sh脚本进行启停。 而我们一般使用的服务&#xff0c;是通过…