C语言—函数
函数
- 库函数
- 自定义函数
- 函数的参数
- 函数的调用
- 函数的嵌套调用和链式访问
- 函数的声明和定义
- 函数递归
- 递归与迭代
- 函数递归的经典题目
维基百科(台湾方面维护的,翻译形式跟大陆有所差异)中对函数的定义:子程序
- 在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method,subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。
- 一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。
C语言中函数的分类:
- 库函数
- 自定义函数
库函数
为什么会有库函数?
- 在学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想把这个结果打印到屏幕上看看。这个时候会频繁的使用一个功能:将信息按照一定的格式打印到屏幕上(printf)。
- 在编程的过程中会频繁的做一些字符串的拷贝工作(strcpy)。
- 在编程时总是会计算n的k次方这样的运算(pow)。
像上面描述的基础功能,它们不是业务性的代码。在开发的过程中每个程序员都可能用的到,为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。
C语言常用的库函数有:IO函数、字符操作函数、内存操作函数、时间/日期函数、数学函数、其他库函数
注:
- 使用库函数,必须包含 #include 对应的头文件。
- 库函数不需要全部记住,学会查询工具的使用即可
查询库函数可以使用以下网址:
- MSDC(软件或者可以查询网址在浏览器中查看(MSDC提供了在线的版本))
- www.cplusplus.com
- http://en.cppreference.com (英文版) 或者http://zh.cppreference.com (中文版)
补充:
- size_t 是无符号整型(unsigned int)
- 将字符数组打印成字符串,用%s打印(%s是以字符串的格式打印)
- strcpy函数是拷贝函数,它连同‘\0’一同拷贝。注意strcpy函数要拷贝的字符数组的大小一定要大于被拷贝字符串的大小
- memset函数是初始化函数。作用是将某一块内存中的内容全部设置为指定的值
- 图形库是用来实现界面的,C语言本身是不够直接写界面的,图形库是别人用C语言、C++等这些编程语言写出来的一些库(跟库函数是一个道理的),使用人家的库就可以画出一些界面(例如一些成熟的库:MFC、QT都是C/C++相关的界面库)
自定义函数
自定义函数和库函数一样,有函数名,返回值类型和函数参数。 但是不一样的是这些都是程序员来设计。这给程序员一个很大的发挥空间。
例如:
函数的组成:
ret_type fun_name(para1, * )
{statement;//语句项
}ret_type 返回类型
fun_name 函数名
para1 函数参数
{statement;//语句项} 函数体
自定义函数的创建及使用:
写一个函数可以找出两个整数中的最大值:
//函数定义
int get_max(int x, int y) //x,y为形参
{int z = 0;if (x > y)z = x;elsez = y;return z;//返回z-返回较大值
}int main()
{int a = 10;int b = 20;//函数调用int max = get_max(a, b); //a,b为实参//int max = get_max(2+5, 3);//int max = get_max(2 + 5, get_max(4, 7));printf("max = %d\n", max);return 0;
}
注:在VS编译器调试过程中,F10是逐过程、F11是逐语句
写一个函数可以交换两个整形变量的内容:
//swap1在被调用的时候,实参传给形参,其实形参是实参的一份临时拷贝,改变形参,不能改变实参
void Swap1(int x, int y)
{int z = 0;z = x;x = y;y = z;
}void Swap2(int* pa, int* pb)
{int z = 0;z = *pa;*pa = *pb;*pb = z;
}int main()
{int a = 10;int b = 20;//写一个函数 - 交换2个整型变量的值Swap1(a, b);//传值调用printf("交换前:a=%d b=%d\n", a, b);Swap2(&a, &b);//传址调用printf("交换后:a=%d b=%d\n", a, b);return 0;
}
注意:千万不能将Swap函数写成void Swap1(int x, int y),这样写是不能将两个整型变量的值进行交换的,因为传参传的是值只会改变函数内部x,y变量中的值而不会改变函数外部调用方两个整型变量的内容(调用方传的参数和函数中参数的地址是不同的)。要想交换两个整形变量的内容就得使用指针来作为函数参数进行传参即可
函数的参数
实际参数(实参):
真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参
形式参数(形参):
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效
注:形参和实参的名字可以相同也可以不同
总结:上述代码中Swap1函数在调用的时候,x,y拥有自己的空间,同时拥有了和实参一模一样的内容。所以可以简单的认为:形参实例化之后其实相当于实参的一份临时拷贝
函数的调用
传值调用
- 函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
传址调用
- 传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
- 这种传参方式可以让函数和函数外边的变量建立起正真的联系,也就是函数内部可以直接操作函数外部的变量。
练习:
- 写一个函数可以判断一个数是不是素数。
int is_prime(int n)
{//2->sqrt(n) 之间的数字int j = 0;for (j = 2; j <=sqrt(n); j++){if (n % j == 0)return 0;}return 1;
}int main()
{//100-200之间的素数int i = 0;int count = 0;for (i = 101; i <= 200; i+=2){//判断i是否为素数if (is_prime(i) == 1){count++;printf("%d ", i);}}printf("\ncount = %d\n", count);return 0;
}
- 写一个函数判断一年是不是闰年。
int is_leap_year(int n)
{return ((n % 4 == 0 && n % 100 != 0) || (n % 400 == 0));
}int main()
{int y = 0;for (y = 1000; y <= 2000; y++){if (is_leap_year(y) == 1){printf("%d ", y);}}return 0;
}
注意:一个函数如果不写返回类型,默认返回int类型
- 写一个函数,实现一个整形有序数组的二分查找。
int binary_search(int a[], int k, int s)
{int left = 0;int right = s - 1;while (left<=right){int mid = (left + right) / 2;if (a[mid] > k){right = mid - 1;}else if (a[mid] < k){left = mid + 1;}else{return mid;}}return -1;//找不到了
}int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };int key = 7;//找到了就返回找到的位置的下标//找不到返回-1//数组arr传参,实际传递的不是数组的本身,仅仅传过去了数组首元素的地址int sz = sizeof(arr) / sizeof(arr[0]);int ret = binary_search(arr, key, sz);if (-1 == ret){printf("找不到\n");}else{printf("找到了,下标是:%d\n", ret);}return 0;
}
注意:
- 数组在作为形参时,写不写数组大小都毫无形象(一般不写大小)。因为传参的时候是数组首元素的地址
- 当函数参数是数组并且没给数组大小时,函数体内是计算不出数组元素个数的,因为数组传参实际上传递的不是数组本身仅仅传过去了数组首元素的地址。只能在传参的时候将数组大小传递过去
- 写一个函数,每调用一次这个函数,就会将num的值增加1
void Add(int*p)
{(*p)++;
}int main()
{int num = 0;Add(&num);printf("%d\n", num);//1Add(&num);printf("%d\n", num);//2Add(&num);printf("%d\n", num);//3return 0;
}
函数的嵌套调用和链式访问
函数和函数之间可以有机的组合的
函数嵌套调用:
注意:函数可以嵌套调用但不可以嵌套定义
函数链式访问
把一个函数的返回值作为另外一个函数的参数。
注:printf函数返回的是打印在屏幕上的字符的个数
函数的声明和定义
当想要在函数定义前使用这个函数需要提前进行函数声明(声明就是告知编译器有这个函数)
函数声明:
- 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,无关紧要。
- 函数的声明一般出现在函数的使用之前。要满足先声明后使用。(前提是函数定义在后面,如果函数定义在前面则不要声明其实函数定义是一种更强有力的声明)
- 函数的声明一般要放在头文件中的。
函数定义:
- 函数的定义是指函数的具体实现,交待函数的功能实现
- 函数的定义一般要放在对应的源文件中的
注意: 函数定义在后面前面进行声明这种用法非常少见
函数的声明和定义的使用:
这样写可以将工程的各个模块功能分开各写各的,各个模块完成后进行合并就可以更快完成项目
将函数实现隐藏起来(静态库隐藏的方式)以及如何生成静态库、导入静态库:
函数声明以及定义
右击项目名称->点击属性
CTRL+F5进行编译,生成静态库
这个文件是sub.c和sub.h文件编译产生的一个静态库(由二进制组成的)
将Sub函数实现的代码隐藏以后就将Sub.lib交给其他人使用,为了让其他人能用,再将sub.h文件(包含了函数的使用方法)交付出去这样就可以知道函数的功能了。
此时得到Sub.lib和sub.h文件时,将这两个文件放到特定项目下就可以使用这个函数功能了
总结:当把函数的声明和实现分离放到.h和.c文件里面,是能做到代码的隐藏的
函数递归
程序调用自身的编程技巧称为递归( recursion)。 递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。 递归的主要思考方式在于:把大事化小
函数递归的使用:
注:使用函数递归时,很容易导致栈溢出
接受一个整型值(无符号),按照顺序打印它的每一位。 例如: 输入:1234,输出 1 2 3 4
void print(unsigned int n)//1234
{if (n > 9){print(n / 10);}printf("%d ", n % 10);
}int main()
{unsigned int num = 0;scanf("%u", &num);//1234 %u用于打印无符号整型//递归 - 函数自己调用自己print(num);//print函数可以打印参数部分数字的每一位return 0;
}
递归的两个必要条件:
- 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
- 每次递归调用之后越来越接近这个限制条件
注意:以上这两个条件是必要条件,只要是递归就一定满足这两个条件,满足这两个条件的不一定是递归
例如:
注:
- 计算机语言所提到得内存中的栈区是存储局部变量和函数形参(存放的是一些临时空间和临时的变量)
- 函数调用都要在栈区上分配一块空间(某函数的栈帧空间)。其中一些局部变量都是先开辟函数栈帧空间然后再在该空间上为局部变量开辟空间
- 如果递归条件太深,每一次都要为一次函数调用开辟空间,最终会导致栈溢出
写递归时:
- 不能死递归,得有跳出条件,每次递归逼近跳出条件。
- 递归层次不能太深。
编写函数允许创建临时变量,求字符串的长度
int my_strlen(char* str)
{int count = 0;while (*str != '\0'){count++;str++;}return count;
}
编写函数不允许创建临时变量,求字符串的长度
int my_strlen(char* str)
{if (*str != '\0')return 1 + my_strlen(str + 1);elsereturn 0;
}
递归与迭代
求n的阶乘。(不考虑溢出)
int main()
{int n = 0;scanf("%d", &n);int i = 0;int ret = 1;//迭代 //循环也称为迭代(循环是一种迭代的方式)for (i = 1; i <= n; i++){ret = ret * i;}printf("%d\n", ret);return 0;
}//递归
int Fac(int n)
{if (n <= 1)return 1;elsereturn n * Fac(n - 1);
}int main()
{int n = 0;scanf("%d", &n);int ret = Fac(n);printf("%d\n", ret);return 0;
}
注:有一些功能可以使用迭代的方式实现,也可以使用递归
求第n个斐波那契数。(不考虑溢出)
//递归
//递归可以求解,但是效率太低
int Fib(int n)
{if (n <= 2)return 1;elsereturn Fib(n - 1) + Fib(n - 2);
}//迭代
int Fib(int n)
{int a = 1;int b = 1;int c = 1;while (n>2){c = a + b;a = b;b = c;n--;}return c;
}
注:
- 这种算法的效率,因为进行了重复大量的计算
- 不会发生栈溢出,因为层次不是太深只是调用的次数比较多
- 系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者(死递归),这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这样的现象称为栈溢出。
- 有时递归算法会导致计算效率低、栈溢出的现象。可以通过将递归改写成非递归或者使用static对象替代nonstatic局部对象。在递归函数设计中,可以使用static对象替代nonstatic局部对象(即栈对 象),这不仅可以减少每次递归调用和返回时产生和释放nonstatic对象的开销,而且static对象还可以保存递归调用的中间状态,并且可为各个调用层所访问。从而解决该问题。
提示:
- 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。
- 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
- 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。
函数递归的经典题目
- 汉诺塔问题
- 青蛙跳台阶问题
补充:VS编译器中底层用的是cl.exe、link.exe编译器
相关文章:

C语言—函数
函数库函数自定义函数函数的参数函数的调用函数的嵌套调用和链式访问函数的声明和定义函数递归递归与迭代函数递归的经典题目维基百科(台湾方面维护的,翻译形式跟大陆有所差异)中对函数的定义:子程序在计算机科学中,子…...

Autosar模式管理实战系列03-基于Davinci工具的WDGM配置
本文框架 前言1.WdgMConfigSet 配置2. 新建监控实体(SE)2.1 新建检测点(Checkpoint)2.2 设置 WdgMInternalTransitions3. WdgMLocalStatusParams配置4. WdgMAliveSupervision配置5. 代码插入指导前言 前面我们介绍了WdgM(看门狗管理)是一个 AutoSAR 的基础模块,负责管理看门…...

AutoML-sklearn and torch
一、auto-sklearn 1.1 环境依赖 额外安装swig 第三方库 linux 支持, mac,windows不支持 1.2 示例代码 time_left_for_this_task 设定任务最大时间 per_run_time_limit 每个子任务最大训练时间 include 可以限制任务训练的模型 import autosklearn.classific…...

《扬帆优配》算力概念股大爆发,主力资金大扫货
3月22日,9股封单金额超亿元,工业富联、鸿博股份、鹏鼎控股分别为3.01亿元、2.78亿元、2.37亿元。 今日三大指数团体收涨,收盘共34股涨停,首要集中于数字经济方向,其间云核算、CPO大迸发。除去5只ST股,算计2…...

机械臂+底盘三维模型从solidworks到moveit配置功能包
文章目录 导出底盘STEP加载机械臂模型组合机械臂和底盘三维模型导出URDF在moveit中进行配置新建工作目录设置ROS工作空间的环境变量进入moveit setup加载URDF文件self-CollisionsPlanning groupsRobot posesControllersSimulationAuthor information生成配置包在rviz中进行可视…...

高并发系统设计:缓存、降级、限流、(熔断)
高并发系统设计:缓存、降级、限流、(熔断) 在开发高并发系统时有三把利器用来保护系统:缓存、降级和限流。 非核心服务可以采用降级、熔断,核心服务采用缓存和限流(隔离流量可以最大限度的保障业务无损)。 缓存 缓…...

《辉煌优配》放量大涨,A股成交额重回万亿!PCB板块继续领跑
多只绩优PCB概念股超跌。 今日A股放量反弹,成交额从头站上万亿关口。芯片板块掀涨停潮,景嘉微、芯原股份20cm涨停,紫光国微、兆易创新、跃岭股份等封板;AI算力、存储器、光模块、云核算等板块全线拉升,板块内个股再度批…...

Vue封装的过度与动画
动画效果 先把样式封装好,然后设置一个动画 不需要vue也能实现的动画的效果,我们只需要判断一下,然后动态的添加和删除类名即可 那能不能不自己写动态,就靠vue 首先我们要靠<transition>标签把需要动画的包裹起来 vue中…...

流量监控-ntopng
目录介绍安装使用介绍 ntopng是原始ntop的下一代版本,ntop是监视网络使用情况的网络流量探测器。ntopng基于libpcap,并且以可移植的方式编写,以便实际上可以在每个Unix平台,MacOSX和Windows上运行。 ntopng(是的&…...

C++ 21 set容器
目录 一、set容器 1.1 简介 1.2 构造和赋值 1.3 大小和交换 1.4 插入和删除 1.5 查找和统计 1.6 set和multiset区别 1.7 内置类型指定排序规则 1.8 自定义数据类型指定排序规则 一、set容器 1.1 简介 ① set容器中所有元素在插入时自动被排序。 ② set容器和multise…...

什么是JWT
JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案。 传统的session认证 http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再一…...

Gradle7.4安装
前置:本文基于IntelliJ IDEA 2022.2.1 、jdk1.8进行安装 目录 1.挑选Gradle版本 2.系统变量设置 1.挑选Gradle版本 gradle兼容性差, 1.跟idea会有版本问题。 2.跟springboot也有兼容问题Spring Boot Gradle Plugin Reference Guide 首先查询版本&…...

【华为OD机试 2023最新 】 箱子之字形摆放(C++ 100%)
文章目录 题目描述输入描述输出描述备注用例题目解析C++题目描述 有一批箱子(形式为字符串,设为str), 要求将这批箱子按从上到下以之字形的顺序摆放在宽度为 n 的空地,请输出箱子的摆放位置。 例如:箱子ABCDEFG,空地宽度为3,摆放结果如图: 则输出结果为: AFG BE CD …...

Matplotlib库入门
Matplotlib库的介绍 什么是Matplotlib库? Matplotlib是一个Python的数据可视化库,用于绘制各种类型的图表,包括线图、散点图、条形图、等高线图、3D图等等。它是一个非常强大和灵活的库,被广泛用于数据科学、机器学习、工程学、…...

学生党用什么蓝牙耳机比较好?300内高性价比蓝牙耳机排行
随着蓝牙技术的发展,蓝牙耳机越来越普及,不同价位、不同性能的蓝牙耳机数不胜数。那么,学生党用什么蓝牙耳机比较好?下面,我来给大家推荐几款三百内高性价比蓝牙耳机,一起来看看吧。 一、南卡小音舱蓝牙耳…...

Lambda 表达式与函数式接口
函数式接口 如果一个接口,只有一个抽象方法,该接口即为函数式接口。函数式接口,即可使用 Lambda 表达式。 如下面的接口 public interface Translate {void translate();}目前该接口的抽象方法为无参数无返回值 Lambda 表达式 无参无返回值…...

后端代码规范
1、报文入参尽量避免使用实体类(如果用实体类接受参数,一定要写好注解,具体用到了实体类的哪一个属性) /*** * Description: 新增玉米观测记录主表信息* param param params* param return 参数* return Result 返回类型* author…...

web自动化测试:Selenium+Python基础方法封装(建议收藏)
01、目的 web自动化测试作为软件自动化测试领域中绕不过去的一个“香饽饽”,通常都会作为广大测试从业者的首选学习对象,相较于C/S架构的自动化来说,B/S有着其无法忽视的诸多优势,从行业发展趋、研发模式特点、测试工具支持&…...

while实现1到100相加求和-课后程序(JavaScript前端开发案例教程-黑马程序员编著-第2章-课后作业)
【案例2-7】while实现1到100相加求和 一、案例描述 考核知识点 while循环语句 练习目标 掌握while循环语句。 需求分析 1-100之间的数相加求和,本案例通过while循环语句来实现。 案例分析 效果如图2-10所示。1-100所有数的和 具体实现步骤如下: 在&l…...

Thingsboard(2.4 postgresql版)数据库表结构说明
本文描述的表结构是根据thingsboard2.4(postgresql版)数据库中整理出来的,不一定完整,后续有新的发现再补充文档。 一、数据库E-R关系 Thingsboard2.4社区版共22个表,主要包括实体信息表、关系信息表、字典表和系统配…...

IDS反病毒与APT的具体介绍
文章目录一,IDS1. 什么是IDS?2. IDS和防火墙有什么不同?3. IDS工作原理?4. IDS的主要检测方法有哪些详细说明?5. IDS的部署方式有哪些?6. IDS的签名是什么意思?签名过滤器有什么作用?…...

while do..while验证用户名和密码-课后程序(JavaScript前端开发案例教程-黑马程序员编著-第2章-课后作业)
【案例2-8】while do..while验证用户名和密码 一、案例描述 考核知识点 while、do…while循环语句 练习目标 掌握while语句。do…while循环语句。 需求分析 在网站上登录时会用到表单,让用户属于用户名和密码,输入正确才可以进入,本案例将…...

tmux常用操作指令
创建会话tmux new -s 会话名 恢复会话tmux at -t 会话名 tmux attach -t 会话名 杀死会话tmux kill-session -t 编号 tmux kill-session -t 会话名 查询会话tmux ls tmux list-session 划分窗格划分上下两个窗格: tmux split-window 划分左右两个窗格:…...

【Linux】线程安全
线程安全:在多线程运行的时候,不论线程的调度顺序怎样,最终的结果都是 一样的、正确的,这个线程就是安全的。 保证线程安全的要求: 1. 对线程同步,保证同一时刻只有一个线程访问临界资源。 2.在多线程中使用…...

Redis-mysql 缓存实战
本文基于Springboot,mybatis plus,mysql,redis, Jedis模拟redis缓存实现 目录 1. 添加所需maven依赖 2. 设置数据库及数据表 3. 构建实体类 4. 构建工具类实现 redis 数据库连接池,redis 的读取,写入功…...

蓝桥杯:通电
蓝桥杯: 通电https://www.lanqiao.cn/problems/162/learning/ 目录 题目描述 输入描述 输出描述 输入输出样例 输入 输出 题目分析(最小生成树): AC代码(Java) 题目描述 2015 年,全中国实现了户户通电。作为一名电力建设者࿰…...

一文搞懂 Kubernetes 的 Limits 和 Requests
当在Kubernetes中使用容器时,重要的是要知道所涉及的资源是什么以及如何需要它们。有些进程比其他进程需要更多的CPU或内存。有些是关键的,不应该被饿死。知道了这一点,我们应该正确配置我们的容器和Pod,以获得两者的最佳效果。在…...

【C++】手撕红黑树
文章目录前言一、红黑树的概念二、红黑树的节点结构三、红黑树的插入四、红黑树的调整1、叔叔存在且为红2、叔叔不存在或存在且为黑3、插入完整代码4、总结五、红黑树的验证六、红黑树的删除七、红黑树与 AVL 树的比较八、红黑树的代码实现前言 在网络上流传着这样一张图片&am…...

Java中的CAS实现原理
文章目录一、什么是CAS?二、JAVA中如何实现CAS操作三、CAS在JUC中的运用四、ABA问题一、什么是CAS? 在计算机科学中,比较和交换(Conmpare And Swap)是用于实现多线程同步的原子指令。 它将内存位置的内容与给定值进行…...

什么是华为云对象存储OBS?它有什么优势?
华为对象存储OBS(Object Storage Service)是一种高可用、高可靠、高性能的云存储服务,能够为企业和个人用户提供强大的数据存储和管理功能。本文将对华为对象存储OBS的特点、优势和未来发展进行详细介绍。 一、华为对象存储OBS的特点 1.对象…...