C语言 —— 函数

目录

1. 函数是什么

2. C语言中函数的分类

2.1 库函数

2.2 自定义函数

3. 函数的参数

3.1 实际参数(实参)

3.2 形式参数(形参)

4. 函数的调用

4.1 传值调用

4.2 传址调用

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

5.1 嵌套调用

5.2 链式访问

6. 函数的声明和定义

6.1函数声明

6.2 函数定义

test.h的内容

test.c的内容

7. 函数递归

7.1 什么是递归?

7.2 递归的两个必要条件

7.2.1 练习1

7.2.2 练习2

7.3 递归与迭代

7.3.1 练习3

7.3.2 练习4


1. 函数是什么

数学中我们常见到函数的概念. 比如.

在维基百科中, C语言对于函数的定义是:子程序

在计算机科学中,子程序(英语: Subroutine, procedure,function, routine,method,subprogram,callable unit) ,是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。
一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
前面我们已经了解过一些函数.
比如strlen()用于求字符串长度, strcmp()用于比较两个字符串的大小, printf()用于打印数据内容.
再比如我们自定义的add()用于加法运算.
这些函数都是只负责完成某项特定任务, 相较于其他代码, 具备相对的独立性.

2. C语言中函数的分类

  1. 库函数: C语言中自带的函数
  2. 自定义函数: 我们自己创建的函数

2.1 库函数

为什么会有库函数?

1.我们知道在我们学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能: 将信息按照一定的格式打印到屏幕上 (printf)。
2.在编程的过程中我们会频繁的做一些字符串的拷贝工作 (strcpy)
3.在编程是我们也计算,总是会计算n的k次方这样的运算 (pow)

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

那么都有哪些库函数? cplusplus.com/reference/

简单的总结,C语言常用的库函数都有:

  • IO函数
  • 字符串操作函数
  • 字符操作函数
  • 内存操作函数
  • 时间/日期函数
  • 数学函数
  • 其他库函数

我们尝试学习几个库函数:

strcpy

#include<stdio.h>
#include<string.h>int main()
{char arr1[20] = { 0 };char arr2[] = "hello bit";strcpy(arr1, arr2);printf("%s\n", arr1);//hello bitreturn 0;
}

memset

#include<stdio.h>
#include<string.h>int main()
{char arr[20] = "hello world";memset(arr+6, 'y', 3);printf("%s\n", arr);return 0;
}

注: 但是库函数必须知道的一个秘密就是: 使用库函数,必须包含 #include 对应的头文件

2.2 自定义函数

自定义函数和库函数一样,有 函数名,返回值类型和函数参数.
但是不一样的是这些都是我们自己来设计.

函数的组成:

ret_type fun_name(para1, * )
{statement;//语句项
}ret_type 返回类型
fun_name 函数名
paral 函数参数

我们举一个例子:

写一个函数可以找出两个整数中的最大值。
//函数的定义
int get_max(int x, int y)
{return (x > y ? x : y);
}int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);//求较大值//函数的调用int m = get_max(a, b);printf("%d\n", m);return 0;
}

再举个例子:

写一个函数可以交换两个整形变量的内容。
void Swap(int x,int y)
{int z = 0;z = x;x = y;y = z;
}int main()
{int a = 0;int b = 0;scanf("%d%d", &a, &b);// 交换printf("交换前:a=%d b=%d\n", a, b);Swap(a, b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}

以上代码会有问题: 交换失败, 与预期结果不符.

我们通过调试会看到以下结果:

可以看到, x, y的地址与a, b的地址并不相同, 说明a和b是两个独立的变量, x和y也是两个独立的变量, 它们所在的内存单元都是不同的.

此处确实是交换了x和y, 但是并没有影响到a和b, a和b没有交换.

所以在这里就有一个重要的知识点: 当实参传递给形参的时候,形参是实参的一份临时拷贝. 对形参的修改不会影响实参.

3. 函数的参数

3.1 实际参数(实参)

真实传给函数的参数,叫实参
实参可以是: 常量、变量、表达式、函数等
无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参

3.2 形式参数(形参)

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

所以前面的例子我们可以修改代码以实现整形变量的交换.

void Swap(int* px, int* py)
{int z = *px;//z=a*px = *py;//a=b*py = z;  //b=a
}int main()
{int a = 0;int b = 0;scanf("%d%d", &a, &b);//交换printf("交换前:a=%d b=%d\n", a, b);Swap(&a, &b);printf("交换后:a=%d b=%d\n", a, b);return 0;
}

可以看到, 实现了预期结果.

4. 函数的调用

4.1 传值调用

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

4.2 传址调用

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

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

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

5.1 嵌套调用

#include <stdio.h>void new_line()
{printf("hehe\n");
}void three_line()
{int i = 0;for (i = 0; i < 3; i++){new_line();}
}int main()
{three_line();return 0;
}

函数可以嵌套调用,但是不能嵌套定义.


printf("%d", printf("%d", printf("%d", 43)));

这段C语言代码涉及到嵌套的printf函数调用,看起来可能会有点令人困惑,但我们可以分解它来理解它的执行顺序。

首先,让我们从最内层的printf开始:

printf("%d", 43)

这一行将打印整数43,并返回打印的字符数,这里是2(因为43有两位数字)。所以最内层的printf打印了43并返回2。

接下来,我们有:

printf("%d", printf("%d", 43));

这里,内部的printf被执行,打印43并返回2(字符数)。然后外部的printf使用返回的值2打印一个整数,即2。所以,中间层的printf打印了2并返回1(因为2只有一位数字)。

最后,我们有:

printf("%d", printf("%d", printf("%d", 43)));

这次内部的两个printf分别是43(返回2)和2(返回1)。外部的printf使用返回的值1打印一个整数,即1。所以最外层的printf打印了1并返回1。

综上所述,代码的输出是4321,分别来自于四个printf函数的结果。


5.2 链式访问

把一个函数的返回值作为另外一个函数的参数.
//链式访问
#include <string.h>int main()
{printf("%d\n", strlen("abcdef"));return 0;
}

6. 函数的声明和定义

6.1函数声明

1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。
2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用
3. 函数的声明一般要放在头文件中的。

6.2 函数定义

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

test.h的内容

放置函数的声明

#ifndef __TEST_H__
#define __TEST_H__
//函数的声明
int Add(int x, int y);
#endif //__TEST_H__

test.c的内容

放置函数的实现

#include "test.h"
//函数Add的实现
int Add(int x, int y)
{
return x+y;
}

7. 函数递归

7.1 什么是递归?

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

7.2 递归的两个必要条件

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续.
  • 每次递归调用之后越来越接近这个限制条件.

7.2.1 练习1

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

正常解法:

//%d 是打印有符号的整数(会有正负数)
//%u 是打印无符号的整数int main()
{unsigned int num = 0;scanf("%u", &num);while (num){printf("%d ", num % 10);num = num / 10;}return 0;
}

递归解法:

void print(unsigned int n)
{if (n > 9){print(n / 10);}printf("%d ", n % 10);
}int main()
{unsigned int num = 0;scanf("%u", &num);//1234print(num);    // 接受一个整型值(无符号),按照顺序打印它的每一位。return 0;
}

执行分析:


讲解一个问题: 栈溢出

在上面的递归函数print中, if判断是否可以不要?

void print(unsigned int n)
{// if (n > 9){print(n / 10);}printf("%d ", n % 10);
}

那么这样操作的话, 就会造成这个函数不断的自己调用自己的现象出现, 就会造成栈溢出.

我们进行调试:

当在调试控制台输入1234之后, 会看到.

那么在图中的Stack overflow就是栈溢出的意思.

之前提到过, 内存分为: 栈区, 堆区, 静态区.
栈区用于存放局部变量, 函数的形参等, 那么栈区也负责每一次函数的调用时的空间分配. 也就是说, 每一次函数的调用都会在栈区申请空间.
具体的细节参照后文: 函数栈帧的创建和销毁.

7.2.2 练习2

编写函数 不允许创建临时变量,求字符串的长度。

先看: 求字符串的长度. 即模拟实现strlen

#include <string.h>//int my_strlen(char str[])//参数部分写出数组的形式int my_strlen(char* str)//参数部分写出指针的形式
{int count = 0;//计数,临时变量while (*str != '\0'){count++;str++;//找下一个字符}return count;
}int main()
{char arr[] = "abc";//[a b c \0]//char* int len = my_strlen(arr);printf("%d\n", len);return 0;
}

那么原题的要求针对了我们使用了count这个临时变量, 所以我们使用递归实现.

那么如何思考这个问题以通过递归实现?

我们要通过递归实现求 abc的长度,
也就是 my_strlen("abc");
我们刚才使用非递归实现的时候是首先先拿到第一个字符, 如果不是\0, count就++, 也就是说第一个字符如果不是\0的话它的长度至少是1, 那么 my_strlen求abc的长度就可以变成 1+my_strlen("bc");(把a拆出来, 就是1), 接着往后就是 1+1+my_strlen("c");, 再接着, 就是 1+1+1+my_strlen("");, 那么就变成了 1+1+1+0.

my_strlen("abc");
1+my_strlen("bc");
1+1+my_strlen("c")
1+1+1+my_strlen("")
1+1+1+0
int my_strlen(char* str)
{if (*str != '\0')return 1 + my_strlen(str+1);elsereturn 0;
}

7.3 递归与迭代

7.3.1 练习3

求n的阶乘。(不考虑溢出)

n的阶乘公式表达:

int fac(int n)
{if (n <= 1)return 1;elsereturn n * fac(n - 1);
}

7.3.2 练习4

求第n个斐波那契数(不考虑溢出)
斐波那契数: 1 1 2 3 5 8 13 21 34 55 ...

int Fib(int n)
{if (n <= 2)return 1;elsereturn Fib(n - 1) + Fib(n - 2);
}

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

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

相关文章

24Hibench

1. Hibench 官网 ​ HiBench is a big data benchmark suite that helps evaluate different big data frameworks in terms of speed, throughput and system resource utilizations. It contains a set of Hadoop, Spark and streaming workloads, including Sort, WordCou…

JavaSE | 初始Java(九) | 包的使用

包 包是对类、接口等的封装机制的体现&#xff0c;是一种对类或者接口等的很好的组织方式&#xff0c;比如&#xff1a;一个包中的类不想被其他包中的类使用。包还有一个重要的作用&#xff1a;在同一个工程中允许存在相同名称的类&#xff0c;只要处在不同的包中即可。 可以…

软断言你也学不会

断言是测试用例的一部分&#xff0c;也是测试工程师开发测试用例的核心。断言通常集成在单元测试和集成测试中&#xff0c;断言分为硬断言和软断言。 硬断言是我们狭义上听到的普通断言:当用例运行后得到的[实际]结果与预期结果不匹配时&#xff0c;测试框架将停止测试执行并抛…

Python实时采集Windows CPU\MEMORY\HDD使用率

文章目录 安装psutil库在Python脚本中导入psutil库获取CPU当前使用率&#xff0c;并打印结果获取内存当前使用率&#xff0c;并打印结果获取磁盘当前使用情况&#xff0c;并打印结果推荐阅读 要通过Python实时采集Windows性能计数器的数据&#xff0c;你可以使用psutil库。psut…

AutoCAD 产品设计:图形单位

本文讲解 AutoCAD 产品的图形单位功能产品设计&#xff0c;没有任何代码实现。 使用的 AutoCAD 为 2020 版本 图形单位是什么&#xff1f; 图形单位是用于设置 一些属性数据应该用什么格式显示 的命令&#xff0c;命令标识为 un&#xff08;units&#xff09;。 举个例子。 …

WebGL笔记:绘制多个点,三角形,以及画各种不同的线条,面

绘制多点 1 &#xff09; WebGL 缓冲区 我们在用js定点位的时候&#xff0c;肯定是要建立一份顶点数据的&#xff0c;这份顶点数据是给着色器的&#xff0c;因为着色器需要这份顶点数据绘图然而&#xff0c;我们在js中建立顶点数据&#xff0c;着色器肯定是拿不到的&#xff…

基于SpringBoot的反诈宣传平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

SpringBoot注册web组件

目录 前言 一、注册Servlet组件 1.1 使用SpringBoot注解加继承HttpServet类注册 1.2 通过继承HttpServet类加配置类来进行注册 二、注册Listener组件 2.1 使用SpringBoot注解和实现ServletContextListener接口注册 2.2 ServletContextListener接口和配置类来进行注册 …

【Spring】Spring 创建和使用

Spring 创建和使用 一. 创建 Spring 项目1. 创建⼀个 Maven 项目2. 添加 Spring 框架⽀持3. 添加启动类 二. 存储 Bean 对象1. 创建 Bean2. 将 Bean 注册到容器 三. 获取并使⽤ Bean 对象1. 创建 Spring 上下文2. 获取指定的 Bean 对象3. 使用 Bean Spring 就是⼀个包含了众多⼯…

【vue3】wacth监听,监听ref定义的数据,监听reactive定义的数据,详解踩坑点

假期第二篇&#xff0c;对于基础的知识点&#xff0c;我感觉自己还是很薄弱的。 趁着假期&#xff0c;再去复习一遍 之前已经记录了一篇【vue3基础知识点-computed和watch】 今天在学习的过程中发现&#xff0c;之前记录的这一篇果然是很基础的&#xff0c;很多东西都讲的不够…

python复习

1.python属于解释型语言&#xff0c;解释器逐行解释每一句代码&#xff0c;然后执行 编译型语言需要由编译器生成最终可执行文件再执行 2. #单行注释""" 多行注释 """ 注释快捷键ctrl/ 3.变量是在计算机语言中能储存计算结果或表示某个数据…

Docker介绍与安装

目录 一、Docker 概述 1、什么时Docker 2、Docker的设计宗旨 4、Docker的优点 5、Docker容器和虚拟机的区别 6、 namespace的隔离&#xff08;命名空间&#xff09; 7、 Docker的三个核心概念 7.1 镜像 7.2 容器 7.3 仓库&#xff08;Docker Hapu&#xff09; 二、D…

Sentinel-2波段合成

Sentinel-2波段合成 在上一篇博客中下载了Sentinel-2数据&#xff0c;他有13个波段的.jp2文件&#xff0c;下面选取需要使用的波段进行合成。 导入了B2&#xff08;蓝色&#xff09;、B3&#xff08;绿色&#xff09;、B4&#xff08;红色&#xff09;、B8&#xff08;近红外&…

Linux--网络编程-字节序

进程间的通信&#xff1a; 管道、消息队列、共享内存、信号、信号量。 特点&#xff1a;都依赖于linux内核。 缺陷&#xff1a;无法多机通信。 一、网络编程&#xff1a; 1、地址&#xff1a;基于网络&#xff0c;ip地址端口号。 端口号作用&#xff1a; 一台拥有ip地址的主机…

Windows11安装MySQL8.1

安装过程中遇到任何问题均可以参考(这个博客只是单纯升级个版本和简化流程) Windows安装MySQL8教程-CSDN博客 到官网下载mysql8数据库软件 MySQL :: Download MySQL Community Server 下载完后,解压到你需要安装的文件夹 其中的配置文件内容了如下 [mysqld]# 设置3306端口po…

(c++)类和对象 下篇

目录 1.再次了解构造函数 2. Static成员 3. 友元 4. 内部类 5.匿名对象 6.拷贝对象时的一些编译器优化 1.再次了解构造函数 1.1 构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 class Date { pub…

WARNING:tensorflow:Your input ran out of data; interrupting training. 解决方法

问题详情&#xff1a; WARNING:tensorflow:Your input ran out of data; interrupting training. Make sure that your dataset or generator can generate at least steps_per_epoch * epochs batches (in this case, 13800 batches). You may need to use the repeat() funct…

1.7.C++项目:仿muduo库实现并发服务器之Poller模块的设计

项目完整在&#xff1a; 文章目录 一、Poller模块&#xff1a;描述符IO事件监控模块二、提供的功能三、实现思想&#xff08;一&#xff09;功能&#xff08;二&#xff09;意义&#xff08;三&#xff09;功能设计 四、封装思想五、代码&#xff08;一&#xff09;框架&#…

Springboo整合Sentinel

Springboo整合Sentinel 1.启动Sentinel java -jar sentinel-dashboard-1.8.6.jar2.访问localhost:8080到Sentinel管理界面(默认账号和密码都是sentinel) 3.引入依赖(注意版本对应) <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spr…

stm32 - 中断/定时器

stm32 - 中断/定时器 概念时钟树定时器类型基准时钟&#xff08;系统时钟&#xff09;预分频器 - 时基单元CNT计数器 - 时基单元自动重装寄存器 - 时基单元基本定时器结构通用定时器计数器模式内外时钟源选择 定时中断基本结构时序预分频器时序计数器时序 概念 时钟树 https:…