一.函数的含义和分类
1.函数的定义:子程序
是一个大型程序当中的某部分代码,由一个或多个语句块组成,它负责完成某项特定的任务,相较于其他代码,具有相对的独立性。
它可以接受输入参数,执行操作,并返回一个值。
2.函数的分类
C语言中常把函数分为库函数和自定义函数。
(1)库函数:由编程语言提供的,已经写好的函数,可以直接调用。每个程序员在开发过程当中都有可能用到的,具有基础功能的函数。(为了支持可移植性和提高程序的效率,所以C语言的基础库当中提供了一系列的库函数,方便程序员进行软件开发)
注意:使用库函数,必须要包含头文件(头文件中包含了函数的定义,包含头文件即函数的声明)
(2)自定义函数:程序员根据自己的需求,编写的,用于实现特定功能的函数
二.自定义函数的组成部分
自定义函数和库函数一样,有返回值类型,函数名,函数参数及函数体
ret_type fun_name(paral,...)
{
statement;
}
ret_type:返回类型
fun_name:函数名.
paral:参数
statement:函数体
(一)函数名
函数的名字,建议函数名具有标识的作用,不要随意取,也不要使用拼音
(二)函数的返回值类型
1.函数的返回值:函数执行完后返回给调用者的数据或者结果,它主要关注函数执行的结果或者状态。
2.函数的返回值类型:函数执行完毕后返回给调用者的数据类型,可以是内置类型也可以是自定义类型
3.函数返回这个动作发生时,空间的变化:
(1)局部变量销毁:当函数执行完毕并准备返回时,函数内部定义的局部变量所占用的栈空间将会被释放,这些局部变量的值将不会再有效,因为它们的内存空间会被回收于后续的函数调用或者其他目的。
(2)栈帧收缩:栈帧是函数调用时分配在栈上的一块内存区域,用来存储函数的参数、局部变量及返回信息。函数返回时,栈指针会恢复到函数调用以前的位置,从而释放整个栈帧。
(三)函数的参数
1.函数名括号后的变量是形式参数。
2.形参:用来接收函数调用时传递的实际参数的值。
3.形参的作用:
(1)接受实际参数:形参在函数定义中声明,用于接收函数调用时传递的实际参数的值。这是形参最基本的作用,它使得函数能够获得外部传入的数据或值。
(2)实现函数和外界的交互:通过形参,函数可以接收来自外部的数据,并在函数内部对这些数据进行处理。处理完成后,函数可以通过返回值或者其他方式将处理结果传递给外部,从而实现函数和外界的数据交互。
4.形参是实参的一份“临时拷贝”
在函数调用时,形参才会实例化,实参的值会传递给形参,形参在函数内部拥有自己的存储空间,作为接收实参值的副本。这样,函数内部对形参的任何修改都不会影响到实参本身,保证了实参数据的安全性。简单来说,形参就像是实参的一个“临时替身”,在函数执行的时候存在,函数执行完后就消失了。
5.传值调用和传址调用
(1)传值调用:函数接收的是参数值的副本。函数内部对参数的操作不会影响到原始数据。(根本的是:在函数内部重新开辟一个独有的空间,用来装形参)
(2)传址调用:函数内部接收到的是函数实际参数的地址,而不是参数的副本。函数内部对参数的操作会影响到实际数据。(根本的是:函数内部接收到的是实参的地址空间,里面的形参就是实参)
(3)使用swap函数举例说明
传值调用:
传址调用:
(四)函数体
1.函数体是函数定义的一个部分,它包含了实现函数功能的代码块,函数体由一对大括号{}包围,位于函数参数之后。
2.函数体的主要目的:执行特定的任务和需求,并可能返回一个结果给调用者。
三.函数的嵌套调用和链式访问
1.函数的嵌套调用:在函数内部调用另一个函数,在函数内部使用另外一个函数的功能。
//举例说明
#include <stdio.h>
void new_line()
{
printf("hehe\n");
}
void three_lines()
{
for (int i = 0; i < 3; i++)
{
new_line();
}
}
int main()
{
three_lines();
return 0;
}
注意:函数可以嵌套调用,但是不能嵌套定义,不能在一个函数内部来定义一个函数
2.函数的链式访问:把一个函数的返回值作为另一个函数的参数
四.函数的声明和定义
(一)函数的声明
1.解释:告诉编译器函数的名称、返回类型和参数类型,但是函数是否真的存在,函数声明决定不了。
2.函数的声明一般出现在函数的使用之前,要满足先声明后使用
(二)函数的定义
1.解释:函数的定义是指函数的具体实现,交代函数的功能实现
2.包括返回类型、函数名、参数列表和函数体。
(三)函数声明和函数定义的应用
1.将函数定义放在头文件中,在源文件中包含头文件
这样的好处:
【1】提高代码复用性:函数定义在头文件中,可以被多个源文件共享,无需重复编写。
【2】便于维护:当函数需要修改时,只需在头文件中更新,所有包含该头文件的源文件都会同步更新。
【3】增强可读性:头文件提供了函数声明的集中查看,有助于理解函数的用途和接口。
【4】支持分离编译:源文件只需包含头文件,编译器在编译时可以找到函数声明,链接时再将实现部分链接进来。
2.代码的封装:将不想公开的函数代码变成乱码
将函数的功能实现设置为静态库
五.函数递归
(一)递归的含义
函数递归是指在函数执行的过程中,函数自身直接或间接地调用自身的一种编程技术。递归函数通常用于解决那些可以被分解为若干个子问题且子问题结构与原问题相同的问题。
(二)递归的元素
1.递归出口(基准情况):
(1)递归函数必须有一个或多个递归出口,以防止无限递归。
(2)递归出口通常是在函数的输入满足某个特定条件时触发。
2.递归调用:
(1)在递归函数中,函数会调用自身来处理子问题。
(2)每次递归调用都应该向递归出口靠近,即问题的规模应该逐渐缩小。
3.递归的进展:
每次递归调用都应该以某种方式改变状态或参数,以确保递归能够逐步逼近基准情况。
如果没有适当的进展,递归可能会陷入无限循环
注意:简而言之,递归存在限制条件,并且每次递归都要越来越接近这个限制条件,当满足这个限制条件的时候,递归就不再继续
六.函数的代码练习
(一)素数的判断
写一个函数判断100-200中的数字是不是素数
# include <stdio.h>
# include<math.h>
int judge(int x)
{
int n = 2;
for (n = 2; n < sqrt((double)x); n++)
{
if (x % n == 0)
{
return 0;//不是素数
}
else
return 1;//是素数
}
}
int main()
{
int i = 100;
for (i = 100; i < 200; i++)
{
if (1 == judge(i))
{
printf("%d\n", i);
}
}
return 0;
}
(二)判断闰年
写一个函数,判断3000-5000年哪些年是闰年
#include <stdio.h>
int leap_year(int x)
{
if ((x % 4 == 0 && x % 100 != 0) || (x % 400 == 0))
{
return 1;
}
else
return 0;
}
int main()
{
int i = 0;
for (i = 3000; i <= 5000; i++)
{
if (1 == leap_year(i))
{
printf("%d\n",i);
}
}
return 0;
}
(三)二分查找
写一个函数,实现一个整形有序数组的二分查找
#include <stdio.h>
int binary_search(int* arr1, int sz1, int n)
{
int left = 0;
int right = sz1;
int mid = (left + right) / 2;
while (left <= right)
{
if (n < arr1[mid])
{
right = mid - 1;
mid = (left + right) / 2;
}
else if (n > arr1[mid])
{
left = mid + 1;
mid = (left + right) / 2;
}
else
return mid;
}
return 0;
}
int main()
{
int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int k = 7;
int sz = sizeof(arr) / sizeof(arr[0]);
if (0 == binary_search(arr, sz, k))
{
printf("找不到\n");
}
else
{
int m = binary_search(arr, sz, k);
printf("找到了,下标是:%d", m);
}
return 0;
}
(四)递归练习
1.接受一个整型值,按照顺序打印它的每一位
#include <stdio.h>
void Print(int x)
{
if (x > 9)
{
Print(x / 10);
}
printf("%d\n", x%10);
}
int main()
{
int k = 789654;
Print(k);
return 0;
}
2.编写一个函数,不允许创建临时变量,求字符串的长度
#include <stdio.h>
int my_strlen(const char* x)
{
if (*x == '\0')
{
return 0;
}
return 1 + my_strlen(x + 1);
}
int main()
{
const char* p = "sanhdca";
int m = my_strlen(p);
printf("%d", m);
return 0;
}