【初阶C语言】认识和使用函数

news2024/11/17 0:30:07

1. 函数是什么
2. 库函数
3. 自定义函数
4. 函数参数
5. 函数调用
6. 函数的嵌套调用和链式访问
7. 函数的声明和定义
8. 函数递归

一、什么是函数

在数学中有函数,在C语言中也有函数,我们直接先给出一个定义:

在基维百科中函数被定义为子程序:

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

例如:main函数、printf打印函数等等,都是C语言中的函数。

我们可以把函数比作一个工厂,把东西送进去加工,出来就会变成你想要的产品。

 函数的基本构成:函数名、返回类型、函数参数和函数体


在C语言中,函数有两大类,库函数和自定义函数,下面一一介绍。

二、库函数

1.来源:C语言的基础库中提供了一系列库函数

2.库函数:

         将函数比作加工厂,那么库函数就是已经搭建好的有专属名字的工厂。我们只需要把原材料(数据)送入该工厂,然后接收产品(结果)即可。我们举个例子:

  • 计算字符串的长度:
char arr[]="love";//用数组来存放字符串
#include<stdio.h>
#include<string.h>
int main()
{
  char arr[]="love";//定义的数组(原材料)
  int ret=0;//定义一个变量记录字符串的长度(接收产品)
  ret strlen(arr);//送入工厂
  printf("%d\n",ret);//printf也是库函数
  return 0;
}

我们很清楚的看到,只需要把数组名字arr放入strlen函数中,再用变量ret接收产品就可以。

  • 数组名就是首元素的地址,通过首地址就可以找到整个字符串的内容,所以只需要把arr送入函数里面就可以。

注:
    但是库函数必须知道的一个秘密就是:使用库函数,必须包含 #include 对应的头文件。所以为什么会有#include<stdio.h>(main函数、printf函数等函数需要包含的)、#include<string.h>(strlen函数需要包含)这些东西。下面我们通过库函数的分类大概确定每种库函数需要使用头文件的类别

3.库函数分类

     一系列功能相近的函数

  • IO函数

        输入输出函数---<stdio.h>

  • 字符串操作函数

         strlen strcpy

  • 字符操作函数

        判断字母大小写、转换等等

  • 内存操作函数
  • 时间/日期函数
  • 数学函数

        <math.h>,如:Pow

  • 其他库函数

4.库函数举例 

     pow函数:用来计算次方,如:2^3=8

函数原型

double pow (double base, double exponent);//函数原型

 参数类型:该函数可以计算小数的幂,精度更高,所以也可以计算整形的变量。所以得出来的结果最高可以为小数,所以返回值是double类型。

函数参数:因为可以计算小数,所以参数的类型也可以是double类型。

第一个参数(double base)是底数,第二个参数(double exponent)是幂。

所以我们只需要把原材料(要计算的数)送到函数里面(工厂)就可以得出结果(产品)

 用法:我们现在要计算2^3的结果,只需要送进函数里面就行。

#include<tsdio.h>
#include<math.h>//pow函数需要包含的头文件
int main()
{
  int n=0;//创建一个变量用于接收计算的结果
  //n=pow(2,3);这样也可以,不过编译器会有警告
  n=(int)pow(2,3);
  printf("%d\n",n);
  return 0;
}

(int)为强制转换类型,转换为整形

总结:pow是一个可以计算次方的库函数,需要的头文件是#include<math,h>

strcpy函数:把A中的字符串复制到B中

函数原型:

char * strcpy ( char * destination, const char * source );//函数原型

 返回值类型:返回目标的地址,所以需要用指针接收,又因为是字符串操作函数,所以char

函数参数:第一个参数(destination)是目的地指针(收获地址),就是被复制进去的

                  第二个参数(source)是源头指针(发货的地方),要复制的值

注意:复制的时候会自带一个\0(字符串结束的标志),但是原数组的\0后面的内容还存在,只是不打印。

用法:

#include<stdio.h>

int main()
{
	char A[] = "#############";
	char B[] = "love you";
	printf("交换前A:%s\n", A);
	printf("交换前B:%s\n", A);
	strcpy(A, B);//过程是从B到A
	printf("交换后B:%s\n",A);
	return 0;
}

 因为数组名就是首地址,所以不需要传地址。

总结:strcpy是字符串复制函数,第一个参数是被复制参数,第二个是复制参数

menset函数:初始化函数,用于内存设置

void * memset ( void * ptr, int value, size_t num );//函数原型

 函数参数:第一个参数(ptr)是操作目标;第二个参数(value)是初始化的内容,第三个参数(num)是操作数目

用法:

#include<stdio.h>
int main()
{
	char arr[] = "love you";//原数组
	printf("%s\n", arr);
	memset(arr,'6',3);操作之后
	printf("%s\n",arr);
	return 0;
}

三、自定义函数

前言:将函数比如工厂,库函数就是官方已经制作好的工厂,那么自定义函数则需要我们自己去构造工厂然后自己使用,因为工厂还没有制作好,所以名字我们可以自己起。

1.自定义函数的组成

函数名字+返回值类型+函数参数+函数体

对比库函数来看,自定义函数比库函数多了函数体部分,该部分就是工厂的功能,需要我们自己实现。

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

这里的语句项就是函数体

2.自定义函数举例

(1)写一个函数实现两个数找最大值(传值)

#include <stdio.h>
//get_max函数的设计
int get_max(int x, int y)
{
	return (x > y) ? (x) : (y);
}
int main()
{
	int num1 = 10;
	int num2 = 20;
	int max = get_max(num1, num2);
	printf("max = %d\n", max);
}

(2)写一个函数把两个整数的值交换 

我们先看错误的写法

 原因:把num1和num2的值传给形参x和y,所以x和y只是复制了一份数据。我们在复印件上面修改数据,是不会影响原件的。

我们看正确写法:

#include <stdio.h>
//正确的版本
void Swap2(int* px, int* py)//接收实参的时候,形参是指针变量
{
	int tmp = 0;
	tmp = *px;
	*px = *py;
	*py = tmp;
}
int main()
{
	int num1 = 1;
	int num2 = 2;
	Swap2(&num1, &num2);//注意这里是&num1,&num2
	printf("Swap2::num1 = %d num2 = %d\n", num1, num2);
	return 0;
}

结果:

 原因:把变量num1和num2的地址传递给形参,形参通过地址就可以找到源头,进而直接交换成功。这个称为传址调用

        可以这么理解1:我们把户口的网址传递给别人,别人可以通过登录该网址找到我们的户口,而直接修改;然后是我们把户口复印件给别人,别人在复印件上面修改对我们的户口是没有影响的。

       实质理解2:通过变量的地址可以找到该变量在内存中存放的位置,该位置就是变量数据所在地,直接在所在的修改数据从而达到修改的效果。

四、函数参数和函数调用

1.函数参数:实际参数(实参)和形似参数(形参)

实际参数:

   真实传递给函数的参数,叫实参。可以是变量、表达式、常量和函数,但是必须有确定的值。

举例:

Max(520,num1)//520是常量,num1是变量
Max(3+5,&num1)//3+5是表达式,&num1是变量
Max(printf("%d",num1))//函数形式

形式参数:

    用来接收实参。是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元)。只存在于函数中,出函数范围自动销毁。

举例:

   

2.实参和形参的内存

注:实参和形参所在的内存单元不一样,各自独立 

   我们用代码配图说明:

我们在编译器上面查看: 所以,形参是在函数体中创造的,只存在于函数中,出了函数自动销毁

3.函数调用

     传值调用和传址调用

  • 传值调用:函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参
  • 传址调用:传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。

传值调用:

#include <stdio.h>
//get_max函数的设计
int get_max(int x, int y)
{
	return (x > y) ? (x) : (y);
}
int main()
{
	int num1 = 10;
	int num2 = 20;
	int max = get_max(num1, num2);//传值调用
	printf("max = %d\n", max);
}

传址调用:

#include <stdio.h>
//正确的版本
void Swap2(int* px, int* py)
{
	int tmp = 0;
	tmp = *px;
	*px = *py;

	*py = tmp;
}
int main()
{
	int num1 = 1;
	int num2 = 2;
	Swap2(&num1, &num2);//传址调用
	printf("Swap2::num1 = %d num2 = %d\n", num1, num2);
	return 0;
}

五、函数链式访问和嵌套使用

1.嵌套调用

#include <stdio.h>
void new_line()
{
   printf("hehe\n");//在new_line函数内部又调用了printf函数
                    //称为嵌套调用
}
void three_line()
{
   int i = 0;
   for(i=0; i<3; i++)
   {
    new_line();//在three_line函数内又嵌套调用new_line
   }
}
int main()
{
    three_line();
    return 0;
}

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

void test()
{
  //int Add(int x,int y);//在函数内部嵌套定义
   Add(x,y)//这样子就可以
}
int main()
{
   test();
   return 0;
}

2.链式访问

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

#include <stdio.h>
#include <string.h>
int main()
{
   char arr[20] = "hello";
   int ret = strlen(strcat(arr,"bit"));//strcat的返回值作为strlen的参数
   printf("%d\n", ret);
   return 0;
}
#include <stdio.h>
int main()
{
   printf("%d", printf("%d", printf("%d", 43)));//结果4321
   //注:printf函数的返回值是打印在屏幕上字符的个数,打印几个,返回几
return 0;
}

六、函数的声明和定义

1.函数声明

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

举例:正常写法


#include<stdio.h>
int Add(int x,int y)//该函数的位置在mian函数前面
{
	return x + y;
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d%d",&a,&b);
	int sum = Add(a, b);
	printf("%d\n",sum);
}

警告编译写法:

原因:因为编译器自上往下阅读,当调用Add函数的时候,前面没有遇到过Add函数,所以会有警告,当运行到后面才发现该函数。 

正确做法:在调用该函数前面先声明

int Add(int x,int y);//声明函数写法

 2.函数定义

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

注:函数的定义是一种特殊的声明。

举例:

int Add(int x, int y)
{
	return x + y;
}

这一整部分就是函数的定义

七、函数递归

前言:函数递归是函数部分较难点,用较少的代码表示庞大的过程。

1.我们看官方的定义;

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

总结:自己调用自己,把大事化小。
 

2.递归的两个条件

   因为递归的思想是先把大块的事情不断分成小块的事情,当分到不能再分的时候(条件1),开始从最小块的事情往大块的事情执行,没执行一次,就会更加接近这个条件(条件2)

官方:

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

3.举例

 题目:接受一个整型值(无符号),按照顺序打印它的每一位。

           例如:输入1234          打印结果1 2 3 4

我们先给一个知识点:一个整数n/10=去掉个位剩下的数,如:1234/10=123

一个整数n%10=个位,如:123%10=3

分析:我们发现,要求打印1 2 3 4,不能使用常规方法;如果是打印4 3 2 1的话,就可以写一个循环,对1234连续取模(%)4次就可以了,但是现在顺序反了不可以。

要打印1,需要1%10=1,打印2,12%10=2,打印3,123%10=3,打印4,1234%10=4

我们可以发现:输入1234,需要拆成1,12,123和1234从小的开始计算。

   换个思想就是:打印1 2 3 4就可以拆成这样子:打印(1 2 3)和4,然后打印1 2 3又可以拆成打印(1 2)和3,打印1 2可以拆成打印1和2;1此时不能再拆,这就是限制条件1。很显然,这种思想就是递归的思想。

思路图:

 代码实现:

#include <stdio.h>
void print(int n)
{
   if(n>9)//条件1
   {
     print(n/10);//不断调用自身
   }
   printf("%d ", n%10);//条件2
}
int main()
{
   int num = 1234;
   print(num);
   return 0;
}

解析:只要满足n>9的条件就不断调用,直到不满足便开始打印。

代码图解:

 显而易见:递归把大事化小,用最短的代码表示最复杂的过程


函数递归是一个重难点,需要加大练习。后续会持续补充递归的内容;有时候递归太复杂,可以拆成非递归,也就是迭代。

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

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

相关文章

MyBatisPlus入门到精通-1

MyBatisPlus(简称MP) 这篇博客主要讲解用MyBatisPlus进行三层架构中Dao层的开发 以这个为目的来进行我们的学习 我们会先通过一个概述和入门案例进行快速上手 之后我们再通过对我们原先的案列的问题进行分析 来进一步了解MP操作数据库的知识 快速入门 MP简介 MP是国人开发的…

HEVC并行处理技术介绍

h265 相比 h264 的复杂度 复杂度体现 ○ h265 帧内预测模式增多&#xff0c;h265 包含角度预测、DC 预测、平面模式等 35 种预测模式&#xff0c;远超 h264 的 17 种模式&#xff0c;帧内模式选择的复杂度大大增加&#xff1b; ○ h265 的区域划分方式更加多样化&#xff0c;提…

聊聊STM32串口通讯的话题

STM32 微控制器系列提供了多个串口模块&#xff0c;用于实现串口通讯。其中&#xff0c;STM32HAL 库中的 UART 驱动模块提供了一套方便易用的函数接口&#xff0c;可以用来配置和操作串口。 串口通讯是一种常见的数据传输方式&#xff0c;可以实现微控制器与外部设备或其他微控…

Jetbrains 2023.2教程

IDEA 2023.2 激活演示 Pycharm 2023.2 激活演示 WebStorm 2023.2 激活演示 Clion 2023.2 激活演示 DataGrip 2023.2 PhpStorm 2023.1.4 激活演示&#xff08;2023.2尚未发布&#xff09; RubyMine 2023.2 激活演示 获取方式 仔细看每一个工具演示的图片 本文由 mdnice …

深度探索 Elasticsearch 8.X:function_score 参数解读与实战案例分析

在 Elasticsearch 中&#xff0c;function_score 可以让我们在查询的同时对搜索结果进行自定义评分。 function_score 提供了一系列的参数和函数让我们可以根据需求灵活地进行设置。 近期有同学反馈&#xff0c;function_score 的相关参数不好理解&#xff0c;本文将深入探讨 f…

2023软件设计师中级备考经验分享(文中有资料链接分享)

先摊结论吧&#xff0c;软考中级设计师备考只是备考半个月&#xff08;期间还摆烂了几天&#xff09;&#xff0c;然而成绩如下&#xff1a; 我自己都没想到会这么好的成绩。。。 上午题&#xff1a;推荐把软考通APP里的历年真题刷3-4遍&#xff0c;直接刷真题&#xff0c;然后…

TimescaleDB多节点功能概述

如果您有更大的pb级工作负载&#xff0c;则可能需要多个TimescaleDB实例。TimescaleDB多节点允许您运行和管理数据库集群&#xff0c;这可以为您提供更快的数据摄取&#xff0c;以及对大型工作负载响应更快、更高效的查询。 多节点架构 多节点TimescaleDB允许您将多个数据库连…

【学习笔记】视频检测方法调研

目录 1 引言2 方法2.1 视频目标跟踪2.1.1 生成式模型方法2.1.2 判别式模型方法2.1.2.1 基于相关滤波跟踪2.1.2.2 基于深度学习跟踪 2.2 视频异常检测2.2.1 基于重构方法2.2.2 基于预测方法2.2.3 基于分类方法2.2.4 基于回归方法 2.3 深度伪造人脸视频检测2.3.1 基于RNN时空融合…

UG NX二次开发(C++)-利用UFun函数获取曲面上等参数的点

文章目录 1、前言2、叶片的三维模型3、利用UFun函数获取曲面的三种方法3.1 利用UF_Modl来实现3.2 利用UF_Point来实现3.2 利用UF_So来实现4、结果展示1、前言 在多轴加工过程中(尤其是薄壁零件),由于需要根据工件表面的几何信息以获取切触点,然后生成刀位轨迹,本文以叶片…

UEditor 百度富文本编辑器使用 遇到问题

小小吐槽 碰到前后不分离项目&#xff0c;富文本使用的UEdtior UEditor 点击上传图片转base64 在ueditor.all.js文件中找到这个 callback()函数 这里使用根据图片的url转成base64 UEditore 粘贴图片转base64 UEditor回显图片&#xff08;base64&#xff09; 把ueditor.all…

【Docker】制作Docker私有仓库

文章目录 1. 安装私有镜像仓库2. 镜像仓库可视化3. 参考资料 1. 安装私有镜像仓库 由于之后我们需要推送到私有镜像仓库&#xff0c;我们预先安装好&#xff0c;使用的是Docker公司开发的私有镜像仓库Registry。 下载Registry的Docker镜像&#xff1b; docker pull registry:2使…

【Django+Vue】英文成绩管理平台--20230727

项目需求 能够满足大部分核心需求&#xff08;标绿&#xff09;&#xff1a;报表部分应该比较难。 项目地址 前端编译 https://gitlab.com/m7840/toeic_vue_dist Vue源码 https://gitlab.com/m7840/toeic_vue Django源码 https://gitlab.com/m7840/toeic_python 项目…

docker部署nextcloud+onlyoffice详细教程【外置MySQL】

一、部署环境 系统版本&#xff1a;CentOS Linux release 7.9.2009 (Core) 配置&#xff1a;16C 16G MySQL&#xff1a;5.7.20 二、部署过程 1、安装docker和docker-compose 请百度 2、创建数据库和用户&#xff0c;并授权 因为我使用外置MySQL&#xff0c;所以需要先创…

从零开始学Docker(一):Docker的安装部署

前述&#xff1a;本次学习与整理来至B站【Python开发_老6哥】老师分享的课程&#xff0c;有兴趣的小伙伴可以去加油啦&#xff0c;附链接 Linux 环境&#xff1a;RockyLinux 9 版本管理 Docker引擎主要有两个版本&#xff1a;企业版&#xff08;EE&#xff09;和社区版&#…

C语言爱心代码

代码&#xff1a; #include <stdio.h> int main() { int i, j, k, l, m; char c3; //ASCII码里面 3 就是一个字符小爱心 for (i1; i<5; i) printf("\n"); //开头空出5行 for (i1; i<3; i) { //前3行中间有空隙分开来写 for (j1; j<32-2*i; j) p…

计算机图形学十三加速结构

加速光线追踪 摘要 在上一节中&#xff0c;我们介绍了whited-style 光线追踪的原理&#xff0c;以及实现细节。相比与光栅化中所使用的blinn-phong模型&#xff0c;光线追踪显著提升了图像质量&#xff0c;但是随之而来的问题是渲染速度过慢。因为在判断光线与场景交点的时候…

面试典中典之线程池的七大参数

文章目录 一、七大元素解释1.corePoolSize&#xff08;核心线程数&#xff09;&#xff1a;2.maximumPoolSize&#xff08;最大线程数&#xff09;&#xff1a;3.keepAliveTime&#xff08;线程空闲时间&#xff09;&#xff1a;4.unit&#xff08;时间单位&#xff09;&#x…

MATLAB与ROS联合仿真(慕羽☆)全套开源资料索引

自2021年9月份开始进行MATLAB与ROS联合仿真相关的研究&#xff0c;至2021年12月份研究基本上结束&#xff0c;至今&#xff0c;已经近两年时间&#xff0c;期间曾收到过很多小伙伴的私信&#xff0c;想让我出点教程&#xff0c;期间我也曾多次想要抽点时间出教程&#xff0c;但…

地矿人专属的二次开发工具——地矿Web二次开发平台

工欲善其事必先利其器&#xff0c;随着互联网信息化时代的不断发展&#xff0c;信息系统和管理规范也越来越复杂&#xff0c;现有的软件产品功能会需要重新完善升级或更改&#xff0c;所以使用一套灵活应变可二次开发的软件产品是必不可少的&#xff0c;一款好的二次开发平台可…

【Apollo学习笔记】—— Routing模块

Routing模块功能 Apollo的routing模块读取高精地图原始信息&#xff0c;用于根据输入RoutingRequest信息在base_map中选取匹配最近的点作为导航轨迹的起点和终点&#xff0c;读取依据base_map生成的routing_map作为生成topo_graph的&#xff0c;然后通过Astar算法在拓扑图中搜…