文章目录

每篇前言

1.导语
2.目标
3.知识点

✨函数概述
1.1,函数的定义
1.2,函数的目的
1.3,函数的分类
1.3.1,按照函数定义分类
1.3.2,按照函数形式分类
1.3.3,按照函数作用范围分类

✨函数定义
2.1,无参函数定义
2.1.1,无参函数定义形式
2.1.2,无参函数说明or注意
2.1.3,无参函数示例使用
2.2,有参函数定义
2.2.1,有参函数定义形式
2.2.2,有参函数说明or注意
2.2.3,有参函数示例使用
2.3,空函数定义
2.3.1,空函数定义形式
2.3.2,空函数说明or注意
2.3.3,空函数示例使用

✨函数参数
3.1,函数参数分类
3.1.1,实际参数(实参)
3.1.2,形式参数(形参)
3.2,函数参数调用
3.2.1,传值调用
3.2.2,传址调用
3.2.3,参数调用注意事项

✨函数调用
4.1,函数声明
4.1.1,函数声明引语
4.1.2,函数声明格式
4.1.3,函数声明省略特例
4.1.4,函数声明注意事项
4.2,函数调用的形式
4.3,函数调用的方式
4.3.1,函数语句
4.3.2,函数表达式
4.4,函数调用的过程

✨函数嵌套调用or链式访问
5.1,函数嵌套调用
5.1.1,函数嵌套调用示例
5.1.2,函数嵌套调用示例说明

  5.2,函数链式访问
5.2.1,函数链式访问示例
5.2.2,函数链式访问示例说明

✨函数递归
6.1,函数递归的定义
6.2,函数递归满足条件
6.3,函数递归具体实例
6.3.1,函数递归实例
6.3.2,函数递归实例说明

4.结语

每篇前言

博客主页:小杨爱编程☆

作者简介:2022年博客萌新

作者专栏:小杨带你玩转C语言
我的座右铭:有志者立长志,无志者常立志

作者请求:由于博主水平有限,难免会有错误和不准之处,希望各位小伙伴能够在留言区批评斧正。


导语

本篇博客主要向各位小伙伴们详细介绍C语言的函数部分内容。该篇博客主要将函数内容分为(1)函数概述(2)函数定义(3)函数参数(4)函数调用(5)函数嵌套调用及链式访问(6)函数递归这六个部分给小伙伴们进行讲解,这蕴盖了函数的大部分内容,希望小伙伴们能够通过阅读,对函数有更深的理解,受益良多!不再让函数这部分内容成为你的烦恼!


目标

了解和掌握C语言的函数知识,对函数有全新的认识和理解。
能够灵活运用函数知识进行解决问题,实现程序运行。


知识点

✨函数概述

1.1,函数的定义

函数,是编程语言中可以被重复调用的,功能相对独立的一个程序段。函数是C语言程序的基本组成单元,所有C语言程序都是由一个或者多个函数构成的。


1.2,函数的目的

函数的引入实现的两个功能为:
  1.为了便于结构化,模块化编程
  2.为了解决代码的重复


1.3,函数的分类
1.3.1,按照函数定义分类

1,标准函数

标准函数即为库函数。C语言系统中存在了大量已经设计好了的常用函数,用户可以直接调用。

主要包括:数学函数,如求实数的绝对值函数fabs(),平方根函数sqrt()等;输入输出函数,如前面已经使用过的输入函数scanf()和输出函数printf()等。

注意:在不同的C语言系统中,提供的库函数的数量和功能可能存在差异,但一些基本的库函数是相同的。

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

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

2,自定义函数

自定义函数是指由用户自己根据具体问题从而定义的函数,功能上相对当与其他语言中的过程,用来实现用户的专门功能。

例如:之前我们为了求几个数中的最大值,自己定义了一个max()函数来进行解决这个问题 。


1.3.2,按照函数形式分类

1,无参函数

无参函数是指在调用函数与被调用函数之间没有数据传递。也就是说,在调用这类函数时没有数据需要传递给被调用函数。无参函数一般用来执行一组指定的操作,可以返回或者不返回函数值,在大多数情况下不返回函数值。

2,有参函数

有参函数是指在调用函数和被调用函数之间有数据传递。也就是说,调用函数可以将数据传递给被调用函数使用;调用结束后被调用函数中的数据也可以返回,供调用函数使用。


1.3.3,按照函数作用范围分类

1,内部函数

内部函数,不具有外部链接属性,因此只限于本文件中的函数调用,而不允许其他文件中的函数调用。

2,外部函数

函数,在本质上都具有外部性质(外部链接属性),除了内部函数之外,其余的函数都可以在同一程序中其他源文件的函数所调用。


注意:

1,所有函数均不可以调用main函数。

2,每个函数必须单独定义,不允许嵌套定义,即不能在一个函数的内部再定义另外一个函数。

3,用户自定义函数不能单独运行,可以被main函数或者嵌套函数所调用,也可以调用其他函数。


由此返回目录

✨函数定义

2.1,无参函数定义
2.1.1,无参函数定义形式

2.1.2,无参函数说明or注意

1,类型名指定函数的返回值的类型,省略时,默认函数返回值类型为int型。

2,void表示函数没有参数,一般情况下无参函数不会有返回值,此时函数类型名为void。

3,函数首部的后面不能加分号,它和函数体一起构成完整的定义。


2.1.3,无参函数示例使用

示例:打印一个学生管理系统的菜单

代码如下:

//无参函数的使用#include void print_menu(){printf(" 学生管理系统\n");printf("*************\n");printf("1,增加学生信息\n");printf("2,删除学生信息\n");printf("3,修改学生信息\n");printf("4,显示所有学生信息\n");printf("5,退出学生系统\n");}int main(){print_menu();return 0;}

代码输出:


2.2,有参函数定义
2.2.1,有参函数定义形式

2.2.2,有参函数说明or注意

1,有参函数比无参函数多了一项内容,即形式参数表列。

2,在形式参数列表中给出的参数称为形式参数(简称形参),它们可以是各种类型的变量,各参数之间用逗号分隔开。

3,在进行函数调用时,调用函数将赋予这些形参实际的值。且每个形参前面的类型必须分别写明,不能因形参类型相同而省略。

4,函数体包括一对大括号里的若干条语句,这些语句实现了函数的功能,并用return语句的返回运算的结果。

5,编译系统不检查函数名与形参变量名是否相同,函数名和形参变量名可以相同,但应该避免这种使用,防止人为造成混淆。


2.2.3,有参函数示例使用

示例:输入一个实数n,计算n的阶乘

代码如下:

#define _CRT_SECURE_NO_WARNINGS#includeint Funic(int x){int n = 1;for (int i = 1; i <= x; i++){n *= i;}return n;}int main(){int n = 0;scanf("%d", &n);int ret = Funic(n);printf("%d", ret);return 0;}

示例举例:5 的阶乘为120


2.3,空函数定义
2.3.1,空函数定义形式

2.3.2,空函数说or注意

1,函数体是空的,调用此函数时什么工作也不做。

2,使用空函数,表明以后要在此调用此函数完成相应的功能。

3,空函数的目的是使程序的结构清晰,可读性好,以后扩充功能方便。


2.3.3,空函数示例使用

示例:定义一个 Func()空函数

示例代码:

#include int Func(){}int main(){int ret=Func();return 0}

示例说明:

在调用程序中写上Func(),表明这里需调用一个Func空函数,而这个函数还没有完成,等待日后完成。


由此返回目录

✨函数参数

3.1,函数参数分类
3.1.1,实际参数(实参)

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

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

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

3.1.2,形式参数(形参)

1,形式参数是指函数名后括号中的变量。

2,因形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。

3,形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。


3.2,函数参数调用
3.2.1,传值调用

1,传值调用是把实参的值传递给被调用函数的形参,这是一种单向的数据传递方式。

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

3,当实参是变量,常数,表达式或者数组元素,形参是变量名时,函数传递数据采用的是传值调用。

传值调用实例:加法运算

//传值调用实例#includeint Add(int x, int y){int z = 0;z = x + y;return z;}int main(){int a = 0;int b = 0;scanf("%d %d", &a, &b);//10 20int sum = Add(a, b);//传值调用printf("%d\n", c);return 0;}


3.2.2,传址调用

1,传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。

2,这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操作函数外部的变量。

3,传址调用传递是地址,使形参与实参共享相同的存储单元,这样就可以通过形参直接引用和处理该地址的数据,从而改变实参。
4,当实参是变量,常数,表达式或者数组元素,形参是数组或者指针类型时,函数传递数据采用的是传址调用。

传址调用实例:交换a,b的值

void Swap2(int* px, int* py){int z = 0;z = *px;//z = a;*px = *py;//a=b;*py = z;//b=z}int main(){int a = 10;int b = 20;printf("交换前:a=%d b=%d\n", a, b);Swap2(&a, &b);//传址调用printf("交换后:a=%d b=%d\n", a, b);return 0;}


3.2.3,参数调用注意事项

1,实参的个数。数据类型应与形参一致,否则会出现编译错误。

2,定义函数时,系统并不给形式参数分配存储单元,只有函数被调用时系统才会给形参分配存储单元。

3,在调用结束后,形参所占用的存储单元会被释放。

4,实参和形参即使同名,也会分配不同的存储单元。


由此返回目录

✨函数调用

4.1,函数声明
4.1.1,函数声明引语
  • 在C语言程序中,一个函数的定义可以放在任意位置,当然是相对其它函数而言的,既可以放在主函数main()之前 也可以放在主函数main()后。当同变量一样,函数的调用也遵循先声明后使用的原则 。

  • 调用库函数时,一般需要在程序的开头用”#include”命令 。如调用数学库中的函数,应该在程序的开头加一条命令“#include”。这是因为对该函数的说明等一些信息包括的相应的头文件中,调用某个函数时必须包含相应的头文件

  • 调用用户自己定义的函数,而且该函数与调用函数在同一程序中,被调用函数的位置在调用函数的后面时,应该在调用函数中对被调用的函数进行声明,即向编译系统声明将要调用哪些函数,并将有关信息通知编译系统。


4.1.2,函数声明格式

1,类型名 函数名(参数类型1,参数类型2,…)

int Func(int,int)

2,类型名 函数名(参数类型1 形参1,参数类型2 形参2,…)

int Func(int x, int y)

4.1.3,函数声明省略特例

当满足下面两种情况之一时,在主函数中可以不对被调用函数进行声明 。

1,被调函数定义在主调函数之前时,对被调用函数的声明可以省去。

2,被调函数的返回值类型是整形或字符型时,对被调用函数的声明 可以省去。

4.1.4,函数声明注意事项
  1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。

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

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


4.2,函数调用的形式

函数名(实际参数表列);

注意:

1,实际参数表列中的参数称为实际参数 简称实参,它们可以是常数、变量和表达式。

2,各参数之间用逗号分开,并且实际参数的个数,类型应该与函数形参的个数、类型一致。

3,如果为无参函数,则无实际参函数表列,但括号不能省略。


4.3,函数调用的方式
4.3.1,函数语句

函数语句:把函数调用作为一条语句。

一般形式:函数名(实参表列);

putchar();

注意:

  • 常用于调用一个没有返回值的函数,只要求函数完成一定的操作。
  • 例如程序中用到的printf()和scanf()函数都是以函数语句的方式调用函数的。
4.3.2,函数表达式

函数表达式:函数调用作为表达式中的一部分出现在表达式中 以函数返回值参与表达式的运算。

一般形式:返回值=函数名(实参表列);

int ret=Func(n)

注意:

常用于调用一个有返回值的函数,函数作为表达式的一部分参与运算,最后将他们的运算结果的值赋给返回值。


4.4,函数调用的过程

执行函数调用时,实际上系统会完成以下工作。

1,为被调用函数的所有形参分配内存,再计算实参的值,并一一对应地赋予相应的形参(对于无参函数,不做该项工作)。

2,为函数说明部分中定义的变量分配存储空间,再依次执行函数的可执行语句。当执行到”return(表达式);”语句时,计算返回值 (如果是无返回值的参数,不做该项工作 )。

3,释放在本函数中定义的变量所分配的存储空间(对于static类型的变量,其空间不会被释放),返回主调函数继续执行。


由此返回目录

✨函数嵌套调用or链式访问

5.1,函数嵌套调用
  • 函数调用:函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。
5.1.1,函数嵌套调用示例

示例代码:

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

代码输出:

5.1.2,函数嵌套调用示例说明
  • 该实例先通过主函数调用three_line()函数,然后在three_line()函数里面调用new_line()函数,new_line()输出打印“hello”。
  • 函数可以嵌套调用,但是不能嵌套定义。

5.2,函数链式访问

函数链式访问:把一个函数的返回值作为另外一个函数的参数。

5.2.1,函数链式访问示例

示例代码:

//函数链式访问#include int main(){printf("%d", printf("%d", printf("%d", 43)));//注:printf函数的返回值是打印在屏幕上字符的个数return 0;}

示例输出:

5.2.2,函数链式访问示例说明

要想知道这个示例代码的输出结果,必须知道一个知识点,即printf()函数的返回值是打印在屏幕上字符的个数。该示例流程:printf()先打印43;然后根据43得出字符数为2,即打印2;最后再根据2得出字符数为1,因此最后输出结果为4321。

为了更好地理解printf()函数的返回值:

常用的编程网站:cplusplus.com – The C++ Resources Network


由此返回目录

✨函数递归

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

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


6.2,函数递归满足条件
  • 寻找递归表达式,对所处理的问题进行简化。
  • 寻找递归的出口,在适当位置结束递归调用。

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

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


6.3,函数递归的过程
  • 递推阶段。将原问题不断地分解为新的子问题,逐渐从未知向已知的方向推进,最终到达已知的条件,即递归结束条件,这时递推阶段结束。
  • 回归阶段。从已知条件出发,按照递推的逆过程,逐一求值回归,最终到达递推的开始处,就结束回归阶段,完成递归调用 。

注:

在分析递归函数时,一定要明白递归函数调用自己时只是调用了同名同功能的函数,或者可以理解为调用的是他自己的副本。在c语言中每调用一次函数,系统都会为该函数分配新的内存空间,来存放各种运行时所需要的各种变量及参数,生成一个新的函数实体,直到该函数结束,才会释放其占用的内存空间。递归时每次调用自身都会生成新的形参变量,它们虽同名但不是同一变量。


6.4,函数递归具体实例
6.4.1,函数递归实例

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

例如:输入:1234,输出 1 2 3 4.

实例代码:

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

实例输出:


6.4.2,函数递归实例说明
  • 递推阶段

  • 回归阶段


由此返回目录

结语

各位小伙伴们,函数内容就介绍到这里,下期向各位小伙伴们讲解数组部分内容。敬请期待!

如果各位小伙伴们感觉写的不错,或者阅读后有些许收获的话,麻烦小伙伴们动动你们的小手,给个三连呗(✌关注,✌点赞,✌评论),多多支持一下!!

最后,感谢每一位小伙伴的阅读,让我们一起努力,一起加油,一起进步!