C语言:函数

news2024/11/24 10:49:43

往期文章

  1. C语言:初识C语言
  2. C语言:分支语句和循环语句

目录

  • 往期文章
  • 前言
  • 1. 函数是什么
  • 2. 库函数
  • 3. 自定义函数
  • 4. 函数的参数
  • 5. 函数的调用
  • 6. 函数的嵌套调用和链式访问
    • 6.1 函数嵌套调用
    • 6.2 函数的链式访问
  • 7. 函数的声明和定义
  • 8. 函数的递归
  • 后记

前言

今天来继续更新我们的C语言博客,目前还处于一鼓作气的阶段,干劲十足哈。希望能够把C语言的博客坚持更新完成。
今天我们介绍C语言的函数部分,闲言少叙,我们开始。

1. 函数是什么

数学中我们常见到函数的概念。但是在C语言中,我们也有函数(子程序)的概念。

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

C语言中函数的分类:

  1. 库函数
  2. 自定义函数

2. 库函数

为什么会有库函数?

  1. 我们知道在我们学习C语言编程的时候,总是在一个代码编写完成之后迫不及待的想知道结果,想把这个结果打印到我们的屏幕上看看。这个时候我们会频繁的使用一个功能:将信息按照一定的格式打印到屏幕上(printf)。
  2. 在编程的过程中我们会频繁的做一些字符串的拷贝工作(strcpy)。
  3. 在编程是我们也计算,总是会计算n的k次方这样的运算(pow)。
    像上面我们描述的基础功能,它们不是业务性的代码。我们在开发的过程中每个程序员都可能用的到,为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。
    在这个网站中,我们可以学习库函数的用法。

cplusplus

在这里插入图片描述
但是库函数必须知道的一个秘密就是:使用库函数,必须包含 #include 对应的头文件。

关于库函数,我们先介绍到这里。接下来,我们介绍自定义函数。

3. 自定义函数

我们有了库函数,但库函数不是万能的,很多时候需要我们的自定义函数。
自定义函数和库函数一样,有函数名,返回值类型和函数参数。 但是不一样的是这些都是我们自己来设计。
我们来看一个自定义函数的基本结构:
在这里插入图片描述
我们来设计一个函数,使其可以找到两个数的最大值:

#include <stdio.h>

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);
	return 0;
}

在这里插入图片描述
在这里插入图片描述
函数名,返回类型、参数、和函数体是一个函数的要素。参数可以没有,返回类型可以为空。

我们再来看一个交换两个数字的例子:

#include<stdio.h>
void Swap(int x, int y)
{
	int tmp = 0;
	tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int num1 = 1;
	int num2 = 2;
	Swap(num1, num2);
	printf("Swap::num1 = %d num2 = %d\n", num1, num2);
	return 0;
}

在这里插入图片描述
我们发现,我们的代码按道理是可以实现两个数字的交换,但是却没有实现数字交换,这是为什么呢?
但是,如果我们将代码修改一下:

#include<stdio.h>

void Swap(int *px, int *py)
{
	int tmp = 0;
	tmp = *px;
	*px = *py;
	*py = tmp;
}
int main()
{
	int num1 = 1;
	int num2 = 2;
	Swap(&num1, &num2);
	printf("Swap::num1 = %d num2 = %d\n", num1, num2);
	return 0;
}

我们再来看一下运行结果:
在这里插入图片描述
交换成功。
这是为什么捏?这就需要我们了解一下函数的参数。

4. 函数的参数

函数的参数分为实参和形参两种。
实际参数(实参):
真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
形式参数(形参):
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。

在第一份代码中,我们传递的xy是形参,在第二份代码中,我们传递的&x&y是实参。形参实例化之后其实相当于实参的一份临时拷贝,并不能改变原来的内容。

如果我们把传参比作让张三到李四家去,传形参就好比我克隆了一个张三,到李四家后,李四把克隆的张三打了一顿破相了,但实际的张三并没有受伤。而传递实参,就是张三亲自到李四家去了,李四把张三打破相了,张三真受伤了。

5. 函数的调用

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

所以,如果我们不想要改变原来的参数,我们可以选择传值,如果我们要改变,我们可以选择传地址。两种调用是C语言考试中常考的知识点,也经常会马虎做错,大家一定要注意哦。(我搞错过好多次)

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

6.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;
}

在这里插入图片描述
嵌套调用成功。但是哈,我们在使用嵌套调用的时候,不要太张狂太随意的使用,有时候你套着套着自己就懵逼了,所以,仔细一点,按需使用。
在这里插入图片描述

但是大家一定要注意,函数可以嵌套调用,但不可以嵌套定义,一个函数定义在另一个函数里面是绝对不允许的。

6.2 函数的链式访问

链式访问指的是把一个函数的返回值作为另外一个函数的参数。我们来看一个例子:

#include<stdio.h>

int Add(int a, int b)
{
	return a + b;
}

int Mul(int c, int d)
{
	return c*d;
}
int main()
{
	int a = 2;
	int b = 3;
	int c = 4;
	int k = Mul(c, Add(a, b));
	printf("%d\n", k);
	return 0;
}

在这里插入图片描述
我们可以看到add函数的返回结果作为了mul函数的参数。
我们可以发现函数的链式访问是基于返回值实现的。

我们再来看一个有趣的例子:

#include<stdio.h>

int main()
{
	printf("%d", printf("%d", printf("%d", 43)));
	return 0;
}

这个很链式,那输出结果是什么呢?来看一下:
在这里插入图片描述
4321,如果你第一次接触这个问题,对这个结果一定会感到非常意外。
在这里插入图片描述
为什么会是这样呢?我们来分析一下:
我们知道,printf是库函数。其返回值是写入的字符总数,我们把这个问题搞清楚之后再来分析:
在这里插入图片描述
根据链式调用,最先打印出来的是43,而43是两个字符,所以接下来打印出的是2,而2是一个字符,所以接下来打印出来的是1.所以这道题屏幕上打印出来的是4321,其实如果我们用逗号分割一下的话是,43,2,1.
在这里插入图片描述

7. 函数的声明和定义

函数声明:

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

当我们用代码实现比较复杂的功能的时候,我们会采用分文件的形式,此时,我们通常在.h文件中声明函数,在.c中定义函数。
在这里插入图片描述
在这里插入图片描述
在一个文件中,函数的定义和声明也可以分离,要先声明,在定义,记住,声明函数时后面要加;
我们来看一下:

#include<stdio.h>

void Swap(int *px, int *py);
int main()
{
	int num1 = 1;
	int num2 = 2;
	Swap(&num1, &num2);
	printf("Swap::num1 = %d num2 = %d\n", num1, num2);
	return 0;
}

void Swap(int *px, int *py)
{
	int tmp = 0;
	tmp = *px;
	*px = *py;
	*py = tmp;
}

在这里插入图片描述

8. 函数的递归

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

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

递归是一个好东西,说白话就是不断重复调用自身直到得到想要的。

关于递归有一个十分经典的问题,斐波那契数列:

#include<stdio.h>

int fib(int n)
{
	if (n <= 2)
		return 1;
	else
		return fib(n - 1) + fib(n - 2);
}

int main()
{
	printf("%d\n",fib(8));
	return 0;
}

递归其实也是一种函数的嵌套调用。

但是递归函数消耗是很大的。如果我们要求n很大的时候,时间会很长,我们可以采用如下两种方法解决这样的情况:

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

后记

好的,这篇博客就到这里了,这篇博客好像没有之前那么事无巨细了,博主这两天有些懒惰。博主将努力克服懒惰,请各位督促。
在这里插入图片描述

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

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

相关文章

什么是无线网桥?全方位解析无线网桥及应用场景

欢迎来到东用知识小课堂&#xff01;1.无线网桥介绍无线网桥的主要用途就是用于桥接两个网络&#xff0c;它可以取代网线&#xff0c;在不方便布线的环境或是无法挖沟埋管&#xff0c;环境已经既定无法再去破坏的环境当中通过无线微波的传输方式&#xff0c;进行网络、视频、数…

Android ViewModel,Lifecycles和LiveData组件讲解

文章目录一、ViewModelViewModel基本用法向ViewModel传递参数二、Lifecycles三、LiveDataLiveData的基本用法map和switchMapJetPack是一个开发组件工具集&#xff0c;他的主要目的是帮助我们编写出更加简洁的代码&#xff0c;并简化我们的开发过程。JetPack中的组件有一个特点&…

【C++11】右值引用

右值引用是C11中才被提出来的新概念&#xff0c;而以前的版本中也有引用&#xff0c;但是是指的左值引用。归根结底&#xff0c;左右值引用都是给对象取别名。 1.区分左值和右值 提起左值和右值很多小伙伴可能第一时间会有点小蒙圈&#xff0c;敲了好长时间代码了&#xff0c;对…

【Java基础】—— Java简介(超详细整理,适合新手入门)

​ “作者 久绊A” 专注记录自己所整理的Java、web、sql等&#xff0c;IT技术干货、学习经验、面试资料、刷题记录&#xff0c;以及遇到的问题和解决方案&#xff0c;记录自己成长的点滴。 写给小白看的入门级 Java 基本语法&#xff0c;需要掌握哪些知识点? Java涵盖的知识点…

1个 30多年程序员的生涯经验总结

有人说&#xff1a;一个人从1岁活到80岁很平凡&#xff0c;但如果从80岁倒着活&#xff0c;那么一半以上的人都可能不凡。 生活没有捷径&#xff0c;我们踩过的坑都成为了生活的经验&#xff0c;这些经验越早知道&#xff0c;你要走的弯路就会越少。 在我30多年的程序员生涯里…

部分iphone、安卓手机打开微信小程序不请求、白页问题

前言&#xff1a; 最近项目上发现用户测试小程序体验版打开一直白页&#xff0c;请求没反应&#xff0c;页面不渲染。开始以为是微信小程序某api问题&#xff0c;或者用户微信版本过低&#xff0c;或者用户网络不好&#xff0c;甚至考虑是不是服务器问题&#xff01;因为后端是…

vue3中如何使用JSX?

在绝大多数情况下&#xff0c;Vue 推荐使用模板<template>语法来创建应用。 在 Vue 3 的项目开发中&#xff0c;template 是 Vue 3 默认的写法。虽然 template 长得很像 HTML&#xff0c;但 Vue 其实会把 template 解析为 render 函数&#xff0c;之后&#xff0c;组件运…

【Java多线程】线程的优先级

线程的优先级等级 MAX_PRIORITY&#xff1a;10 MIN _PRIORITY&#xff1a;1 NORM_PRIORITY&#xff1a;5 涉及的方法 getPriority() &#xff1a;返回线程优先值 setPriority(int newPriority) &#xff1a;改变线程的优先级 例&#xff1a; 我们将分线程的优先级设置为…

Java设计模式-迭代器模式Iterator

介绍 根据GoF的定义&#xff0c;迭代器模式提供了一种顺序访问聚合对象的元素而不暴露其底层表示的方法。这是一种行为设计模式。 顾名思义&#xff0c;迭代器有助于以定义的方式遍历对象集合&#xff0c;这对客户端应用程序很有用。在迭代期间&#xff0c;客户端程序可以根据需…

SciPy 教程与安装

SciPy 教程SciPy 是一个开源的 Python 算法库和数学工具包。Scipy 是基于 Numpy 的科学计算库&#xff0c;用于数学、科学、工程学等领域&#xff0c;很多有一些高阶抽象和物理模型需要使用 Scipy。SciPy 包含的模块有最优化、线性代数、积分、插值、特殊函数、快速傅里叶变换、…

小场景解决大问题|明道云在京东方的落地实践

我是来自京东方集团京东方晶芯科技有限公司的季旭。很荣幸给各位分享我们京东方集团和明道云之间的合作情况。 关于京东方晶芯 在分享之前&#xff0c;我首先给各位介绍一下我们公司。京东方集团是1993年4月成立的&#xff0c;以半导体显示为核心技术&#xff0c;在物联网创新…

聚观早报 | 硅谷大数据龙头Palantir扩招;滴滴出行恢复新用户注册

今日要闻&#xff1a;硅谷大数据龙头Palantir扩招&#xff1b;美团无人机去年完成配送超10万单&#xff1b;滴滴出行恢复新用户注册&#xff1b;PS VR2将于2月22日全球同步上市&#xff1b;改款特斯拉 Model 3加州路测谍照曝光硅谷大数据龙头Palantir扩招 1 月 17 日消息&#…

【Javascript】面向对象编程,this,原型与原型链,类与实例

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录对象中的方法/thisthis使用bind函数原型原型链类与实例class对象中的方法/this 面向对象&#x…

【C语言】小王带您实现文件操作(简单图示讲解)

说到文件操作&#xff0c;大家会第一印象想到不就是电脑硬盘中创建文件&#xff0c;写入数据吗&#xff0c;键盘、鼠标就可以搞定&#xff0c;那么接下来我要告诉你的是C语言也可以实现文件操作哦&#xff01;&#xff01;&#xff01; 目录 前言 一、为什么要使用文件操作 …

模拟卷.C

1.分支_sine之舞 样例输入 3 样例输出 ((sin(1)+3)sin(1-sin(2))+2)sin(1-sin(2+sin(3)))+1 2.数组_和最大子序列 样例输入 5 3 -2 3 -5 4 样例输出 4 3.二维数组_星辰大海 样例输入 2 2 S. #T 2 RD DR 3 S.# .#. .T# 3 RL DDD DDRR 样例输出 I get …

文档管理系统采用电子签名的优势

DocuWare文档管理系统始终提供最高级的安全性&#xff0c;保护我们客户的机密文件和数据。 现在&#xff0c;我们与信任服务提供商ValidatedID 集成&#xff0c;电子签名又向前迈出了重要的一步。每当组织需要证明签名是真实的并确保文档未被更改时&#xff0c;都可以使用这些…

开源mybatis神器

什么是通用 Mapper&#xff1f; 它是一个可以方便的使用 Mybatis 进行单表的增删改查优秀开源产品。它使用拦截器来实现具体的执行 Sql&#xff0c;完全使用原生的 Mybatis 进行操作。在 Github 上标星 5.9K&#xff01; 为什么要用 Mapper&#xff1f; 它提供了所有单表的基…

《SQL基础》07. 约束

SQL-约束约束常见约束案例外键约束删除/更新行为约束 概念&#xff1a;约束是作用于表中字段上的规则&#xff0c;用于限制存储在表中的数据。 目的&#xff1a;保证数据库中数据的正确性、有效性和完整性。 分类&#xff1a; 约束描述关键字非空约束限制该字段的数据不能为n…

动态内存管理(2)

TIPS 1. scanf读取与空格&#xff1a; 我们都知道&#xff0c;scanf()在从输入缓冲区里面读取数据的时候&#xff0c;如果中间碰到了空格&#xff0c;那么就会直接停下来&#xff0c;而如果在最前面有个空格&#xff0c;直接无视空格。 2. scanf()读取与\n&#xff0c;如果是…

【论文精选】TPAMI2020 - PFENet_先验引导的特征富集网络_小样本语义分割

【论文精选】TPAMI2020 - PFENet_先验引导的特征富集网络_小样本语义分割 精选精析&#xff1a; 【论文原文】&#xff1a; Prior Guided Feature Enrichment Network for Few-Shot Segmentation (当前引用次数&#xff1a;184) 【论文代码】&#xff1a; https://github.co…