当前位置: 首页 > news >正文

【C语言初阶】函数

在这里插入图片描述
在这里插入图片描述

文章目录

  • 💐专栏导读
  • 💐文章导读
  • 🌷函数是什么?
  • 🌷函数的分类
    • 🌺库函数
    • 🌺自定义函数
  • 🌷函数的参数
  • 🌷函数的调用
  • 🌷函数的嵌套调用和链式访问
    • 🌺嵌套调用
    • 🌺链式访问
  • 🌷函数的声明和定义
    • 🌺函数声明
    • 🌺函数定义
  • 🌷函数递归
    • 🌺什么是递归?
    • 🌺递归的两个必要条件
  • 🌷递归与迭代

💐专栏导读

🌸作者简介:花想云,在读本科生一枚,致力于 C/C++、Linux 学习。

🌸专栏简介:本文收录于 C语言初阶专栏,本专栏主要内容为C语言的初阶知识的全套讲解,包含初识C语言、函数、数组、分支与循环、操作符、指针初阶的讲解。

🌸相关专栏推荐:C语言进阶系列数据结构与算法

💐文章导读

本章为大家介绍了C语言中函数的相关概念。如:函数是什么、函数的分类、函数的形参与实参、函数的调用、函数的递归、函数的递归与迭代等等。

🌷函数是什么?

数学中我们常见到函数的概念,那么C语言中的函数是什么呢?
来看看维基百科中对于函数的定义:

在计算机科学中,函数 = 子程序(英语:Subroutine, procedure, function, routine,
method,subprogram, callable unit),是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。png)

  • 我是这么理解函数的:函数——具有某种功能的代码块

一个程序中我们经常会用到某种功能,如两数相加,如果每次都在需要用到时实现,那么就显得过于繁杂。此时,我们就可以将两数相加的功能封装起来,在需要使用的地方进行函数调用即可。

🌷函数的分类

🌺库函数

  • 我们知道在我们学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能:将信息按照一定的格式打印到屏幕上(printf)。

  • 在编程的过程中我们会频繁的做一些字符串的拷贝工作(strcpy)。

  • 在编程是我们也计算,总是会计算n的k次方这样的运算(pow)。

像上面我们描述的基础功能,它们不是业务性的代码。我们在开发的过程中每个程序员都可能用的到,为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。

  • 那么库函数都有哪些呢?这里我给大家提供一个使用频率非常高的网站:
    http://www.cplusplus.com(简易轻便,缺点是全英文但并不影响阅读)
    在里面我们可查询到各种各样的库函数及其使用方法。

库函数数量很多,无法一一列举,只能对其分类:

  • IO函数

    字符串操作函数

    字符操作函数

    内存操作函数

    时间/日期函数

    数学函数

    其他库函数

我们参照文档,简单认识几个库函数:

  • strlen:求字符串长度的函数;
size_t strlen(const char* str)//size_t本质就是unsigned int
#include <stdio.h>//包含库函数printf所对应的头文件<stdio.h>
#include <string.h>//包含库函数strlen所对应的头文件<string.h>int main()
{int str1[]= "zhangsan";int str2[] = "lisi";int len = strlen(str1);printf("%d",len);return 0;
}
  • strcpy:将一个字符串中的内容拷贝到另一个字符串;
char* strcpy(char* destination, const char* source);
#include <stdio.h>//包含库函数printf所对应的头文件<stdio.h>
#include <string.h>//包含库函数strcpy所对应的头文件<string.h>int main()
{int str1[]= "zhangsan";int str2[] = "lisi";int len = strlen(str1);strcpy(str1, str2);printf("%d",len);return 0;
}

注意:使用库函数必须包含 #include 对应的头文件。

🌺自定义函数

如果库函数能干所有的事情,那还要程序员干什么?

所以更加重要的是自定义函数

自定义函数和库函数一样,有函数名返回值类型函数参数

但是不一样的是这些都是我们自己来设计。这给程序员一个很大的发挥空间。

函数的构成

函数返回值类型  函数名  (参数1,参数2,参数3......)
{//...函数体
}

举例

  • 写一个函数可以计算两个数之和
#include <stdio.h>
//add函数的设计
int add(int x, int y) {return x + y;
}
  • 写一个函数可以找出两个整数中的最大值
#include <stdio.h>
//get_max函数的设计
int get_max(int x, int y) {return (x > y) ? (x) : (y);
}

🌷函数的参数

C语言中函数的参数一般分为两种:

  • 实际参数(实参)

    真实传给函数的参数,叫实参

    实参可以是:常量、变量、表达式、函数等

    无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。

  • 形式参数(形参)

    形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数
    形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。

举例:交换两个变量num1和num2中的值;

#include <stdio.h>
//实现成函数,但是不能完成任务
void Swap1(int x, int y) {int tmp = 0;tmp = x;x = y;y = tmp;
}
//正确的版本
void Swap2(int* px, int* py) {int tmp = 0;tmp = *px;*px = *py;*py = tmp;
}
int main()
{int num1 = 1;int num2 = 2;Swap1(num1, num2);printf("Swap1::num1 = %d num2 = %d\n", num1, num2);Swap2(&num1, &num2);printf("Swap2::num1 = %d num2 = %d\n", num1, num2);return 0;
}

运行结果
运行结果如图所示,至于为什么两个函数的运行结果不相同,原因如下:

  • 首先 Swap1 和 Swap2 函数中的参数 x,y,px,py 都是形式参数。在main函数中传给 Swap1 的 num1 ,num2 和传给 Swap2 函数的 &num1 , &num2实际参数
  • 在调用函数Swap1时,形参 x 和 y 拥有自己的空间,同时拥有了和实参一模一样的内容。
    此时x=1,y=2,当函数执行完之后,x=2,y=2。x和y确实交换了值,但与num1和num2并没有什么关系
  • Swap2不同,形参 px、py指针变量(int*为指针类型,后期会讲),对它俩进行解引用后再将值交换,实际上交换的就是num1和num2所在空间的内容。

所以我们可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝

🌷函数的调用

函数的调用分为两种:

  • 传值调用

函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。(例如Swap1)

  • 传址调用

传址调用是把函数外部创建变量内存地址传递给函数参数的一种调用函数的方式。这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。(例如Swap2)

🌷函数的嵌套调用和链式访问

函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。

🌺嵌套调用

我中有你,你中有我

  • 在一个函数中可以调用另外一个函数

举例

#include<stdio.h>
int add(int x, int y)//函数1
{return x + y;
}int addplus(int x, int y,int z)//函数2
{int sum = 0;sum = add(x, y);//调用函数1return sum + z;
}
int main()
{int num1 = 1;int num2 = 2;int num3 = 3;int pr=addplus(num1, num2, num3);printf("%d\n", pr);return 0;
}

🌺链式访问

把一个函数的返回值作为另外一个函数的参数

举例

#include<stdio.h>
int add(int x, int y)//add函数返回值是int类型
{return x + y;
}#include <stdio.h>
int main()
{int num1 = 1;int num2 = 2;printf("%d\n", add(num1,num2));printf("%d", printf("%d", printf("%d", 43)));//结果是啥?//注:printf函数的返回值是打印在屏幕上字符的个数return 0;
}

🌷函数的声明和定义

🌺函数声明

  • 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函 数声明决定不了;

  • 函数的声明一般出现在函数的使用之前。要满足先声明后使用

  • 函数的声明一般要放在头文件中的。

举例

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>//申请一个结点
SLTNode* BuySLTNode(SLTDataType data);
//创建一个链表,包含数据为0~n
SLTNode* CreateSList(int n);
//释放内存
void SLTDestroy(SLTNode** pphead);
//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType data);
//尾删
void SLTPopBack(SLTNode** pphead);
//...

🌺函数定义

  • 函数的定义是指函数的具体实现,交待函数的功能实现

举例

SLTNode* BuySLTNode(SLTDataType data)
{SLTNode* newNode = (SLTNode*)malloc(sizeof(SLTNode));//检查是否申请成功if (newNode == NULL){perror("malloc fail");exit(-1);}//对newNode进行初始化newNode->data = data;newNode->next = NULL;//返回申请成功的结点return newNode;
}SLTNode* CreateSList(int n)
{...//过程省略
}void SLTDestroy(SLTNode** pphead)
{...//过程省略
}void SLTPushBack(SLTNode** pphead, SLTDataType data)
{...//过程省略
}
void SLTPopBack(SLTNode** pphead)
{...//过程省略
}

🌷函数递归

🌺什么是递归?

  • 程序调用自身的编程技巧称为递归( recursion)

递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解, 递归策略 只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。

递归的主要思考方式在于:把大事化小

🌺递归的两个必要条件

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续;

  • 每次递归调用之后越来越接近这个限制条件。

举例
接受一个整型值(无符号),按照顺序打印它的每一位(例如:输入:1234,输出 1 2 3 4)。

#include <stdio.h>
void print(int n) 
{if (n > 9){print(n / 10);}printf("%d ", n % 10);
}
int main()
{int num = 1234;print(num);return 0;
}

🌷递归与迭代

函数在被调用时会建立函数栈帧(简单理解为建立函数栈帧就是在内存中申请了一块中间来运作函数执行)。有些情况下,递归并不是特别高效,例如遇到斐波那契数列的问题时,就不再那么实用(虽然递归的实现方式易于理解代码)。

举例

//求斐波那契数列中第n个数
int fib(int n) {if (n <= 2)return 1;elsereturn fib(n - 1) + fib(n - 2);
}

这段代码理论上可以求出任何n的结果,但是实际上当n等于50左右程序就会挂掉。原因是这段代码的算法对内存的消耗巨大。

在调试 fib 函数的时候,如果你的参数比较大,那就会报错: stack overflow(栈溢出)这样的信息。系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象我们称为栈溢出

那如何解决上述的问题?

  1. 递归改写成非递归
  2. 使用static对象替代 nonstatic 局部对象。在递归函数设计中,可以使用 static对象替代nonstatic局部对象(即栈对象),这不 仅可以减少每次递归调用和返回时产生和释放 nonstatic 对象的开销,而且static 对象还可以保存递归调用的中间状态,并且可为各个调用层所访问;

举例
下面代码就采用了非递归的方式来实现:

//求第n个斐波那契数
int fib(int n) {int result;int pre_result;int next_older_result;result = pre_result = 1;while (n > 2){n -= 1;next_older_result = pre_result;pre_result = result;result = pre_result + next_older_result;}return result;
}
  • 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。

  • 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些

  • 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。

在这里插入图片描述

点击下方个人名片,可添加博主的个人QQ,交流会更方便哦~
*** ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓***

相关文章:

【C语言初阶】函数

文章目录&#x1f490;专栏导读&#x1f490;文章导读&#x1f337;函数是什么&#xff1f;&#x1f337;函数的分类&#x1f33a;库函数&#x1f33a;自定义函数&#x1f337;函数的参数&#x1f337;函数的调用&#x1f337;函数的嵌套调用和链式访问&#x1f33a;嵌套调用&a…...

CentOS 7安装redis6.2.6(包括服务开机自启和开放端口)

CentOS 7安装redis6.2.61. 官网下载redis文件2. 校验安装依赖2.1 安装系统默认版本gcc2.2 升级gcc版本3. 解压编译安装4. 修改配置redis.conf4.2 设置密码4.3 绑定ip&#xff08;可选&#xff09;5. 启动redis服务并测试5.2 测试安装是否成功5.3 redis开机自启配置6.开放防火墙…...

基于注解的自动装配~

Autowired&#xff1a;实现自动装配功能的注解 Autowired注解能够标识的位置&#xff1a; 标识在成员变量上&#xff0c;此时不需要设置成员变量的set方法标识在成员变量对应的set方法上标识在为当前成员变量赋值的有参构造上使用注解进行自动装配&#xff0c;只要在其成员变量…...

【深度学习】【分布式训练】Collective通信操作及Pytorch示例

相关博客 【深度学习】【分布式训练】Collective通信操作及Pytorch示例 【自然语言处理】【大模型】大语言模型BLOOM推理工具测试 【自然语言处理】【大模型】GLM-130B&#xff1a;一个开源双语预训练语言模型 【自然语言处理】【大模型】用于大型Transformer的8-bit矩阵乘法介…...

Spring常用注解说明

目录 1.常用注解 2.特别说明 3.xml及注解方式 1.常用注解 (1) SpringBootApplication (2) ControllerRestControllerRequestMappingRequestParamPathVariableGetMappingPostMappingPutMappingDeleteMappingResponseBodyRequestBodyCrossOrigin (3) ConfigurationBeanServ…...

13-C++面向对象(纯虚函数(抽象类)、多继承、多继承-虚函数、菱形继承、虚继承、静态成员)

虚析构函数 存在父类指针指向子类对象的情况&#xff0c;应该将析构函数声明为虚函数&#xff08;虚析构函数&#xff09; 纯虚函数 纯虚函数&#xff1a;没有函数体且初始化为0的虚函数&#xff0c;用来定义接口规范 抽象类&#xff1a; 含有纯虚函数的类&#xff0c;不可以实…...

Android DataBinding 自定义View实现数据双向绑定

看不懂的可以先看看单向数据绑定&#xff1a;Android DataBinding数据变化时自动更新界面_皮皮高的博客-CSDN博客 然后再确定已经启动了dataBinding的情况下&#xff0c;按下面的顺序来&#xff1a; 首先创建一个自定义View&#xff1a; import android.content.Context imp…...

网络安全中的渗透测试主要那几个方面

渗透测试中主要有软件测试和渗透测试。 1、测试对象不同 软件测试&#xff1a;主要测试的是程序、数据、文档。 渗透测试&#xff1a;对象主要为网络设备、主机操作系统、数据库系统和应用系统。 2、测试内容不同 软件测试&#xff1a;主要工作内容是验证和确认&#xff0c;发…...

Cursor:GPT-4 驱动的强大代码编辑器

Cursor &#xff08;https://www.cursor.so/&#xff09;是 GPT-4 驱动的一款强大代码编辑器&#xff0c;可以辅助程序员进行日常的编码。下面通过一个实际的例子来展示 Cursor 如何帮助你编程。这个例子做的事情是网页抓取。抓取的目标是百度首页上的百度热搜&#xff0c;如下…...

C/C++中for语句循环用法及练习

目录 语法 下面是 for 循环的控制流&#xff1a; 实例 基于范围的for循环(C11) 随堂笔记&#xff01; C语言训练-计算1~N之间所有奇数之和 题目描述 输入格式 输出格式 样例输入 样例输出 环形方阵 干货直达 for 循环允许您编写一个执行特定次数的循环的重复控制结构。…...

AnimatorOverrideController说明

unity-AnimatorOverrideControllerhttps://docs.unity.cn/cn/current/ScriptReference/AnimatorOverrideController.html 用于控制动画器重写控制器的接口。 动画器重写控制器的用途是重写某个控制器的动画剪辑&#xff0c;从而为给定化身定制动画。 在运行时基于相同的 Anim…...

1.4、第三阶段 MySQL数据库

root数据库技术 一、数据库理论 1 什么是数据库技术 数据库技术主要研究如何组织、存储数据&#xff0c;并如何高效地提取和处理数据。 2 什么是SQL SQL&#xff08;Structured Query Language&#xff09;结构化查询语言 SQL是操作数据库的命令集&#xff0c;也是功能齐全的…...

LeetCode:202. 快乐数

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340;算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;202. 快乐数 题目描述&#xff1a;编写一个算法来判断一个数 n 是不是快…...

Android 14 新功能之 HighLights:快速实现文本高亮~

日常开发中可能会遇到给 TextView 的全部或部分文本增加高亮效果的需求&#xff0c;以前可能是通过 Spannable 或者 Html 标签实现。 升级 Android 14 后就不用这么迂回了&#xff0c;因其首次引入直接设置高亮的 API&#xff1a;HighLights。需要留意的是 HighLights API 和 …...

[渗透教程]-004-嗅探工具-Nmap

文章目录 Nmap介绍基本操作进阶操作Nmap介绍 nmap是一个网络扫描和主机检测工具,它可以帮助用户识别网络上的设备和服务。获取主机正在运行哪些服务,nmap支持多种扫描,UDP,TCP connect(),TCP SYN(半开扫描) ftp代理,反向标志,ICMP,FIN,ACK扫描,ftp代理,反向标志,ICMP. 可以用于…...

大数据技术之Hive SQL题库-初级

第一章环境准备1.1 建表语句hive>-- 创建学生表 DROP TABLE IF EXISTS student; create table if not exists student_info(stu_id string COMMENT 学生id,stu_name string COMMENT 学生姓名,birthday string COMMENT 出生日期,sex string COMMENT 性别 ) row format delim…...

常见HTTP状态码汇总

文章目录1xx: 信息2xx: 成功3xx: 重定向4xx: 客户端错误5xx: 服务器错误1xx: 信息 状态码描述100 Continue服务器仅接收到部分请求&#xff0c;但是一旦服务器并没有拒绝该请求&#xff0c;客户端应该继续发送其余的请求。101 Switching Protocols服务器转换协议&#xff1a;服…...

蓝桥杯刷题冲刺 | 倒计时15天

作者&#xff1a;指针不指南吗 专栏&#xff1a;蓝桥杯倒计时冲刺 &#x1f43e;马上就要蓝桥杯了&#xff0c;最后的这几天尤为重要&#xff0c;不可懈怠哦&#x1f43e; 文章目录1.年号字串2.裁纸刀3.猜生日1.年号字串 题目 链接&#xff1a; 年号字串 - 蓝桥云课 (lanqiao.c…...

【差分数组】

差分数组一维差分差分数组的作用差分矩阵结语一维差分 输入一个长度为 n 的整数序列。接下来输入 m个操作&#xff0c;每个操作包含三个整数 l,r,c&#xff0c;表示将序列中 [l,r] 之间的每个数加上 c &#xff0c;请你输出进行完所有操作后的序列。 输入格式 第一行包含两个…...

2022年NOC软件创意编程(学而思)决赛小学高年级组scratch

2022NOC决赛图形化小高组 一、选择题 1.运行下面的程序,最终“我的变量”的值是多少? 2.希望定义一个函数如下,可以让角色旋转指定的圈数。里面空缺的地方填上什么数字比较合适? 3.运行程序,在舞台上可以看见几个角色 ? 4.运行程序,角色会依次说什么 ? 5.我们都知…...

Spring Boot 实现流式响应(兼容 2.7.x)

在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩

目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2

每日一言 今天的每一份坚持&#xff0c;都是在为未来积攒底气。 案例&#xff1a;OLED显示一个A 这边观察到一个点&#xff0c;怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 &#xff1a; 如果代码里信号切换太快&#xff08;比如 SDA 刚变&#xff0c;SCL 立刻变&#…...

Pinocchio 库详解及其在足式机器人上的应用

Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库&#xff0c;专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性&#xff0c;并提供了一个通用的框架&…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...