函数的介绍

1.函数的概念

在C语言中也有函数的概念,有些翻译为:子程序,这种翻译更为准确。C语言的函数就是一个完成某项特定的任务的一小段代码。这段代码是有特殊的写法和调用方法的。

C语言的程序其实是有无数个小的函数组合而成的,也可以说:一个大的计算任务可以分解成若干个较小的的函数完成。同时一个函数如果能完成某项特定任务的话,这个函数也是可以复用的,提升了开发软件的效率。

在C语言中一般会见到的两类函数:

  • 库函数
  • 自定义函数

2.库函数

2.1标准库和头文件

C语言标准库中规定了C语言的各种语法规则,C语言并不提供库函数,C语言的国际标准ANSI C规定了一些常用的函数的标准,被称为标准库,那不同的编译器厂商根据ANSI提供的C语言标准就给出了一系列函数的实现。这些函数被称为库函数。

前面内容中学到的 printf 和 scanf 都是库函数,库函数也是函数,不过这些函数已经是现成的,我们只要学会就能直接使用了。有了库函数,一些常见的功能就不需要我们自己写了,在一定程度上提升了效率;同时库函数的质量和执行效率上都更有保证。

各种编译器的标准库中提供了一系列的库函数,这些库函数根据功能划分,都在不同的头文件中进行了声明。

2.2库函数的使用方法

2.2.1头文件的包含

库函数是在标准库中对应的头文件中声明的,所以库函数的使用,应该包含对应的头文件,不包含是可能会出现一些问题。

#include<stdio.h>
#include<math.h>
int main()
{double ret = sqrt(16.0);printf("%lf\n", ret);return 0;
}

 输出结果:

3.自定义函数

了解了库函数,其实自定义函数更加重要。

3.1函数的语法形式

自定义函数和跨函数是一样的,形式如下:

type name(形式参数)

{

}

  • type   是函数返回类型
  • name 是函数名
  • 括号中是放形式参数
  • { }括起来的是函数体

type 是用来表示函数计算结果的类型,有时候返回的类型可以是 void ,表示什么都不返回

name 是为了方便使用函数,就像人一样,有了名字方便称呼,而函数有了名字方便调用,所以函数名尽量根据函数的功能起的有意义。

函数的参数也可以是 viod ,明确表示函数没有参数。如果没有参数,要交代清楚参数的类型和名字,以及参数的个数。

3.2函数的例子

写一个加法函数,完成2个整型变量的加法操作。

int Add(int x, int y)
{return x + y;
}int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);int ret=Add(a, b);printf("%d\n", ret);return 0;
}

我们给函数取名为:Add ,函数 Add 需要接收2个整型类型的参数,函数计算的结果也是整型。 

4.形参和实参

在函数使用过程中,把函数的参数分为:实参和形参

4.1实参

在上面的代码中,main 函数中的 a,b称为实际参数,简称实参。实际参数就是真实传递给函数的参数。

4.2形参

在上面代码中,函数名 Add 后括号中的 x 和 y ,称为形式参数,简称形参

为什么是形式参数呢?实际上,如果只是定义了 Add 函数,而不去调用的话,Add 函数的参数 x 和 y 只是形式上存在,不会向内存申请空间,不会真实存在,所以叫做形式参数。形式参数只有在函数被调用的过程中为了存放实参传递过来的值,才会向内存申请空间,这个过程就是形参的实例化。

4.3实参和形参的关系

虽然我们提到了实参是传递给形参的,他们之间是有联系的,但是形参和实参各自是独立的内存空间。

在上面的调试中可以看见虽然 x 和 a 的值相等,但是可以发现两个的地址不一样,同样 y 和 b 也是值一样,地址不一样。所以我们可以理解为形参是实参的一份临时拷贝。

5.return语句

在函数的设计中,函数中经常会出现 return 语句,return 语句的注意事项:

  1. return后边可以是一个数值,也可以是一个表达式,如果是表达式则先执行表达式,再返回表达式的结果。
  2. return后边也可以什么都没有,直接写 return ;这种写法适合函数返回类型是void的情况。
  3. return返回的值和函数返回类型不一致,系统会自动将返回类型的值转换为函数的返回类型。
  4. return 语句执行后,函数就彻底返回,后边的代码不再执行。
  5. 如果函数里面存在 if 等分支语句,则要保证每种情况下都有 return 返回,否则会出现编译错误。 

6.数组做函数参数

在使用函数解决问题时,可能会将数组作为参数传递给函数,在函数内部对数组进行操作。

例如:写一个函数将一个整型数组内容全部置为 -1:

void set_arr(int arr2[],int sz2)
{int i = 0;for (i = 0; i < sz2; i++){arr2[i] = -1;}
}
void print_arr(int arr[], int sz) {int i = 0;for (i = 0; i < sz; i++){printf("%d ", arr[i]);}printf("\n");
}int main()
{int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };int sz1 = sizeof(arr1) / sizeof(arr1[0]);//打印数组的所有内容print_arr(arr1,sz1);//将数组里面的元素全部设置为-1set_arr(arr1,sz1);print_arr(arr1, sz1);return 0;
}

7.嵌套调用和链式访问

7.1嵌套调用

嵌套调用就是函数之间的相互调用,正是因为函数的相互调用,最后才写出了大型的程序。

例如:实现输入的某年某月有多少天?

get_year(): 来判断是否为闰年

get_days(): 调用get_year确定是否为闰年后,再根据月份来计算这个月有多少天?

//判断是否为闰年
int get_year(int year)
{if (((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0)))return 1;elsereturn 0;
}int get_days(int y, int m) {int arr[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };int day = arr[m];if (get_year(y) && m == 2)//闰年返回1{day++;}return day;
}
int main()
{int year = 0;int month = 0;scanf("%d %d", &year, &month);int d = get_days(year, month);printf("%d\n",d);return 0;
}

7.2链式访问

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

int main() 
{/*size_t len = strlen("abcdef");printf("%zd\n", len);*/printf("%zd\n", strlen("abcdef"));return 0;
}

可以从上面的代码中看见,将注释了的两行代码转换为一条语句,这里就构成了函数的链式访问。 

int main()
{printf("%d", printf("%d", printf("%d", 43)));
//  打印1      打印2返回1(1个字符)  打印43,返回2(2个字符)
//输出结果为 4321printf("%d ", printf("%d ", printf("%d ", 43)));
//中间加了空格则输出结果为:43 3 2(空格也算是一个字符)
}

8.函数的声明和定义

8.1单个文件

一般我们使用函式的时候,直接将函数写出来就使用了。

例如:判断一年是否为闰年的代码:

红色框里面是函数的定义,而绿色框里面是函数的调用。

这样的顺序没有什么问题,当我们将定义放在调用之后就会出现问题。

就会有这样的错误:

这是因为C语言编译器对源代码进行编译时,从第一行往下扫描,但遇到第251行 is_leap_year调用时,在前面并没有发现 is_leap_year 函数的定义,就会报出上面的警告。

解决的方法:

  1. 将函数的定义写在函数调用的前面
  2. 在函数调用之前写上函数的声明

  • 函数的调用一定要满足:先声明后调用,有时函数的定义是需要写在后面的,这时我们就可以用函数的声明
  • 函数的定义也是一种特殊的声明,所以函数的定义放在调用之前也是可以的。

8.2多个文件

一般情况下,函数的声明和类型的声明放在头文件中,函数的实现会放在源文件中。

分模块写代码的好处:

  1. 逻辑清晰
  2. 方便写作

8.3 static 和 extern

staticextern 都是C语言的关键字。

static静态的意思,可以用来:

  • 修饰局部变量
  • 修饰全局变量
  • 修饰函数

extern 是用来声明外部符号的。

在了解 staticextern 之前先了解一下:作用域生命周期

作用域 是程序设计的概念,通常来说,一段程序代码中所用到的名字并不总是有效(可用)的,而限定这个名字的可用性的代码范围就是这个名字的作用域。

  1. 局部变量的作用域时变量所在的局部范围。
  2. 全局变量的作用域是整个项目。

生命周期 指的是变量的创建(申请内存)到变量的销毁(收回内存)之间的一个时间段。

  1. 局部变量的生命周期是:进入作用域变量的船舰,生命周期开始,出作用域生命周期结束。
  2. 全局变量的生命周期是:整个程序的生命周期。

8.3.1 static 修饰局部变量:

int test()
{int i = 0;i++;printf("%d ", i);
}int main()
{int i = 0;for (i = 0; i < 5; i++) {test();}return 0;
}
int test()
{static int i = 0;i++;printf("%d ", i);
}int main()
{int i = 0;for (i = 0; i < 5; i++){test();}return 0;
}

将上面的两组代码进行对比的话就可以更好的体现出 static 修饰局部变量的作用。

代码1中的test函数中的i属于局部变量,当主函数调用 test 函数时,局部变量i开始创建,生命周期开始,之后++,再打印,出函数时变量的生命周期结束,释放内存。

代码2中其实运行一下结果就可以发现具有累加的作用,其实主函数调用test函数时变量创建,出函数时也不会被销毁,重新进入函数也不会再次创建变量,直接上次累积的值上面进行计算。

结论:

static 修饰局部变量改变了变量的生命周期,生命周期改变的本质是改变了变量的存储类型,本来一个局部变量是存储在内存的栈区的,当被 static 修饰后就存储到了静态区。存储到静态区的变量和全局变量一样,生命周期就和程序的生命周期一样了,只有程序结束,变量才会销毁,内存才回收,但是作用域不变。

内存:

建议:如果一个变量出了函数之后,我们还想保留值等下次进入函数继续使用,就可以使用static来修饰。

8.3.2 static 修饰全局变量

当我们这样写时输出结果为:2018;而当我们在全局变量的前面加上 static'时,此时的结果又是怎样的呢?

此时就会出现(链接型错误)报错,这是因为:extern 是用来声明外部符号的,如果一个全局的符号在X文件中定义的,在Y文件中想使用的,就可以使用 extern 来进行声明后再使用。

结论:

一个全局变量被static修饰,使得这个全局变量只能在本源的文件中使用,其他文件不能使用。

本质原因是全局变量默认是具有外部链接属性的,在外部文件中想使用,只要适当的声明就可以使用,但是全局变量被static修饰后,外部连接属性就变成里的内部链接属性,只能在自己所在的文件中使用,其他文件就算是进行了声明也无法正常使用。

建议:

如果一个全局变量只想在所在的文件中使用,不想被其他文件使用是,就可以使用static来修饰全局变量。

8.3.3 static 修饰函数

我们可以发现:其实static修饰函数static修饰全局变量是一模一样的,一个函数在整个工程都可以被使用,但是当被 static 修饰时,只能在本文件中使用,其他文件无法使用。

建议:

一个函数只想在本文件中被使用,不想被其他文件使用,就可以用 static 来修饰。

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

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

相关文章

MES汽车零部件制造生产监控看板大屏

废话不多说&#xff0c;直接上效果 预览效果请在大的显示器查看&#xff0c;笔记本可能有点变形 MES汽车零部件制造生产监控看板大屏 纯html写的项目结构如下 主要代码分享 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UT…

JS—原型与原型链:2分钟掌握原型链

个人博客&#xff1a;haichenyi.com。感谢关注 一. 目录 一–目录二–原型三–原型链 二. 原型 什么是原型&#xff1f; 每个JavaScript对象都有一个原型&#xff0c;这个原型也是一个对象。比方说 function Person(name) {this.name name; } let person new Person(&quo…

TCP 协议

文章目录 TCP 协议简介数据包格式TCP的特性连接机制确认与重传缓冲机制全双工通信流量控制差错控制拥塞控制 端口号三次握手数据传输四次挥手抓包参考 本文为笔者学习以太网对网上资料归纳整理所做的笔记&#xff0c;文末均附有参考链接&#xff0c;如侵权&#xff0c;请联系删…

二分查找的应用

什么时候用二分查找&#xff1f; 数据具有二段性的时候 第一题&#xff1a; 题解代码&#xff1a; class Solution { public:int search(vector<int>& nums, int target) {int left 0,right nums.size()-1;while(left<right){int mid left (right-left)/2;//中…

cmake 之 CMakeLists.txt 中的函数是从哪里来的

我们都知道&#xff0c;cmake会解释执行 CMakeLists.txt 以及其他 *.cmake 脚本&#xff0c; 这里先给出一个“先验” 的知识点&#xff1a; 任何一个独立脚本或脚本函数命令的执行&#xff0c;都是通过 CPP 函数 RunListFile(...) 调用的 void cmMakefile::RunListFile(cmL…

QT 实现信号源实时采集功能支持频谱图,瀑布图显示

利用QT实现信号源实时采集功能&#xff0c;先看效果 支持双光标显示 &#xff0c;功率测量&#xff0c;带宽测量&#xff0c;载噪比测量&#xff0c;波形框选&#xff0c;水平移动等功能&#xff0c;下载链接 https://download.csdn.net/download/ZuoYueXian/90501632 实现方…

【Kafka】深入了解Kafka

集群的成员关系 Kafka使用Zookeeper维护集群的成员信息。 每一个broker都有一个唯一的标识&#xff0c;这个标识可以在配置文件中指定&#xff0c;也可以自动生成。当broker在启动时通过创建Zookeeper的临时节点把自己的ID注册到Zookeeper中。broker、控制器和其他一些动态系…

神聖的綫性代數速成例題10. N維矢量綫性運算、矢量由矢量組綫性表示、N個N維矢量相關性質

N 維矢量綫性運算&#xff1a; 設&#xff0c;是維矢量&#xff0c;是數。加法&#xff1a;。數乘&#xff1a;。 矢量由矢量組綫性表示&#xff1a; 設是n維矢量&#xff0c;若存在一組數&#xff0c;使得&#xff0c;則稱矢量可由矢量組綫性表示。 N 個 N 維矢量相關性質&…

在CentOS 7.6中安装openGauss 5.1.0 (Preview)数据库并使用Navicat进行远程连接的过程记录

部署环境 华为云Flexus应用服务器 操作系统&#xff1a;CentOS 7.6 openGauss版本&#xff1a;openGauss 5.1.0 (Preview) 参考文档 官方安装文档&#xff1a; https://docs.opengauss.org/zh/docs/5.1.0/docs/InstallationGuide/%E4%BA%86%E8%A7%A3%E5%AE%89%E8%A3%85%E6%B…

SysOM 可观测体系建设(一):万字长文解读低开销、高精度性能剖析工具livetrace

可观测性是一种通过分析系统输出结果并推断和衡量系统内部状态的能力。谈及可观测性一般包含几大功能&#xff1a;监控指标、链路追踪、告警日志&#xff0c;及 Continues Profiling 持续剖析能力。对于操作系统可观测&#xff0c;监控指标可以帮助查看各个子系统&#xff08;I…

Shell脚本学习笔记:从入门到变量(一)

前言 最近在看 Shell 脚本相关的内容&#xff0c;以下是我从入门到变量部分的整理笔记&#xff0c;内容有点多&#xff0c;但都是干货。 先从基础开始&#xff0c;再逐步深入。 一、Shell 脚本入门 1. Linux 如何控制硬件&#xff1f; Linux 靠内核操作硬件&#xff08;CP…

Linux应用:进程间通信

linux的进程间通信概述 进程间通信&#xff08;IPC&#xff0c;Inter - Process Communication&#xff09;是指在不同进程之间进行数据交换和同步的机制。由于每个进程都有自己独立的地址空间&#xff0c;直接共享内存存在困难&#xff0c;因此需要专门的 IPC 机制来实现进程…

el-input 不可编辑,但是点击的时候出现弹窗/或其他操作面板,并且带可清除按钮

1.focus“getFocus”鼠标聚焦的时候写个方法&#xff0c;弹窗起来 getFocus(){ this.定义的弹窗状态字段 true;} 2.点击确定的时候&#xff0c;数值赋值到el-input的输入框,弹窗取消&#xff08;this.定义的弹段字端 false&#xff09; 3.但是会有个问题就是el-input 不可点…

Weblogic未授权远程命令执行漏洞复现

1 漏洞简介 Weblogic是Oracle公司推出的J2EE应用服务器&#xff0c;CVE-2020-14882允许未授权的用户绕过管理控制台的权限验证访问后台&#xff0c;CVE-2020-14883允许后台任意用户通过HTTP协议执行任意命令。使用这两个漏洞组成的利用链&#xff0c;可通过一个GET请求在远程W…

海康SDK协议在智联视频超融合平台中的接入方法

一. 海康SDK协议详解 海康SDK协议原理 海康SDK协议是海康威视为开发者提供的一套软件开发工具包&#xff0c;用于与海康设备&#xff08;如摄像头、NVR、DVR等&#xff09;进行通信和控制。其核心原理包括&#xff1a; 网络通信&#xff1a;基于TCP/IP协议&#xff0c;实现设…

五模型对比!Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量时间序列预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 光伏功率预测&#xff01;五模型对比&#xff01;Transformer-GRU、Transformer、CNN-GRU、GRU、CNN五模型多变量时间序列预测(Matlab2023b 多输入单输出) 1.程序已经调试好&#xff0c;替换数据集后&#xff0c;仅运…

20250319在荣品的PRO-RK3566开发板的buildroot系统下使用集成的QT应用调试串口UART3

stty -F /dev/ttyS3 115200 -echo cat /dev/ttyS3 & echo serialdata > /dev/ttyS3 20250319在荣品的PRO-RK3566开发板的buildroot系统下使用集成的QT应用调试串口UART3 2025/3/19 14:17 缘起&#xff1a;在荣品的PRO-RK3566开发板的buildroot系统下&#xff0c;在命令…

Git 使用笔记

参考链接&#xff1a; 创建版本库 - Git教程 - 廖雪峰的官方网站 Git使用教程,最详细&#xff0c;最傻瓜&#xff0c;最浅显&#xff0c;真正手把手教 - 知乎 命令使用 cd f: 切换目录到 F 盘 cd gitCxl 切换目录到 gitCxl 文件夹 mkdir gitCxl 创建新文件…

Xilinx系列FPGA视频采集转HDMI2.0输出,基于HDMI 1.4/2.0 Transmitter Subsystem方案,提供6套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我已有的 GT 高速接口解决方案我已有的FPGA图像处理方案 3、详细设计方案设计框图硬件设计架构FPGA开发板输入Sensor之-->OV5640摄像头动态彩条Video In To AXI4-S…

机器学习面试重点第二部分(动画版)

​ 目录 ​ 第一章、聚类算法 ​1.1 K-means 聚类 ​1.1.1 算法​编辑流程 1.1.2 优缺点 ​1.1.3 应用场景 ​1.2 层次聚类 ​1.2.1 算法流程 1.2.2 优缺点 ​1.2.3 应用场景 ​1.3 DBSCAN ​1.3.1 算法流程 1.3.2 优缺点 ​1.3.3 应用场景 1.3.4. 参数 ε&…