【C语言】函数基础知识点总结

目录

1.函数定义

2.函数类型

2.1自定义函数

2.2库函数

3.实参与形参

4.函数的调用

5.函数的声明

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

6.1嵌套调用

6.2链式访问

7.函数递归


博主水平有限,文章中若有错误,望读者们斧正。

1.函数定义

维基百科中,函数的定义是

“在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method,
subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组
成。它负责完成某项特定任务,而且相较于其他代 码,具备相对的独立性。
一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软
件库。”
以上定义很好的说明了函数的特性:相对的独立性,对过程的封装和细节的隐藏。以后我们在做项目时,对函数的使用可以说是无处不在,甚至没有函数我们难以完成整个项目。由此可见学习函数是非常有必要的,下面我们开始函数的学习吧。

2.函数类型

c语言中函数分为两类:1.自定义函数 ,2.库函数

2.1自定义函数

函数在c语言中是可以自定义的,那如何定义一个函数呢?

void fun_name(int x)
{
	函数语句;
}

1)void是函数返回类型(如果函数在运行完成后需要给主程序返回一个值,我们就要用相应的类型去定义这个函数。要返回整形,就用 int去定义函数,不需要返回我们就用void类型)

2)fun_name是函数的名字,名字要起的与函数功能匹配

3)括号里的int是参数类型,x是函数参数(参数可以有一个或多个,但参数要尽量少),当然也可以没有参数,让程序直接运行函数的语句

void test()
{
	函数语句;
}

4)如果函数有返回值,调用函数时我们要创建一个变量接收返回值。没有变量时,我们直接调用就行 

//1有返回值时

int fun(int x)
{
	语句;
}

int main()
{
	int a = 0;
	int ret = fun(a);
	return 0;
}

//2没有返回值时

void fun(int x)
{
	语句;
}

int main()
{
	int a = 0;
	fun(a);
	return 0;
}

我们举一个例子:自定义一个函数,找出两个数中的最大值。那我们接下来就要创建一个有两个参数的函数,并且函数将会返回两个数中的最大值,返回值是整形,所以我们用 int类型创建一个名为Max的函数。要怎么调用我们创建的函数呢?我们只要写上函数名,再将要求最大值的两个数以逗号间隔,放进括号中

Max(a, b)
//找出两个数中的最大值
int Max(int x, int y)//设定两个函数参数
{
	return (x > y ? x : y);
}


int main()
{
	int a = 10;
	int b = 20;
//当程序运行到这时,函数会被程序调用
//函数返回最大值,我们创建 max 变量接收最大值
	int max = Max(a, b);
	printf("%d", max);
	return 0;
}

这样Max 函数就写好了。 

2.2库函数

如果我们想实现一些基础功能(例如打印信息,计算字符串的长度,计算x 的k次方)我们还要自己写函数吗?当然是不用的,这样一来我们写代码的效率有太低了,什么都要自己写。所以c语言为我们提供了一系列的库函数,这些库函数很好地提高了我们代码的效率。
关于库函数,我们可以通过下面这几个网站来学习。

cplusplus.com - The C++ Resources Network

cppreference.com(英文)
cppreference.com(中文)
在这些网站中我们能查阅到库函数的基本信息,我们通过printf函数在cplusplus中的描述,看懂这些网站是怎么描述一个函数的,然后 举一反三,更好的学习其他函数
(函数名与函数功能)

红色框为函数原型,我们可以从中了解到函数的参数类型,返回类型,这个非常重要,决定了我们要给函数传参的类型以及要用什么类型接收返回值。
如果函数参数较多,介绍的Parameters中会具体说明每个参数的意思。这里举strcpy这个例子

 (函数返回类型与应用举例)

函数在不同情况下会返回不同的值,在Return Vaule中都具体介绍了。如果看完后还是不懂要怎么使用这个库函数,我们还能通过Example,看看别人是怎么使用这个函数的。 

 最后我们在使用库函数时一定要引用头文件。可以看到,printf的头文件是熟悉的stdio.h,我们就要在程序开始处加上#include<stdio.h>(这里建议把头文件都写在开头处)

 3.实参与形参

在函数中 x,y被称作形式参数(形参),a,b则是实际参数(实参)

结合刚刚的Max函数理解形参与实参

//找出两个数中最大值
int Max(int x, int y)
{
	return x > y ? x : y;
}


int main()
{
	int a = 10;
	int b = 20;
	int c = Max(a, b);
	printf("%d", c);
	return 0;
}

它们有什么区别?形参其实是实参的一份临时拷贝。换言之,形参不等于实参,在函数运行时,程序为形参单独开辟内层空间,在程序结束时,程序销毁形参。两者的内存空间不同,所以改变形参的值不能改变实参。

我们再来一个例子

void Swap(int x, int y)
{
	int z = x;//两数交换时创建第三个变量,就像交换酱油与醋,我们要找第三个瓶子
	x = y;
	y = z;
}

int main()
{
	int a = 10;
	int b = 20;
	printf("打印前:a = %d,b = %d\n", a, b);
	Swap(a, b);
	printf("打印后:a = %d,b = %d\n", a, b);

}

这是一个交换函数,用它我们交换a与b的值,那结果是什么?

两个数没有发生交换,为什么?这是因为形参是实参的临时拷贝,改变形参的值不能改变实参的值。要实现两数交换,我们可以把地址传给形参,对地址解引用,找到实参,从而间接的改变实参。

4.函数的调用

把地址作为实参传给形参的调用函数方式称为传址调用,把实参的值作为实参传给形参的方式叫做传值调用。

来看看交换函数的正确写法

void Swap(int* x, int* y)
{
	int z = *x;
	*x = *y;
	*y = z;
}

int main()
{
	int a = 10;
	int b = 20;
	printf("打印前:a = %d,b = %d\n", a, b);
	Swap(&a, &b);
	printf("打印后:a = %d,b = %d\n", a, b);

}

运行结果 

 所以在写函数时要先考虑调用方式是传值调用还是传值调用。要想通过函数改变外部变量的值,我们通常传址调用,反之我们就使用传值调用。

5.函数的声明

如果我们在调用函数时,将函数写到了主函数的后面,程序会报一个警告,因为程序在运行时总是从上到下运行的,程序要调用一个函数,但是它却不认识这个函数,更不要说去调用它了。所以我们在调用函数时要满足先声明后使用,声明就是告诉程序这个函数叫什么,返回类型是什么,参数有什么...而声明一般会放到头文件中。(这里先简单介绍下,之后我会接着补充怎么具体声明)

#include<stdio.h>

//这就是声明Swap函数的格式
void Swap(int* x, int* y);

int main()
{
	int a = 10;
	int b = 20;
	printf("打印前:a = %d,b = %d\n", a, b);
	Swap(&a, &b);
	printf("打印后:a = %d,b = %d\n", a, b);

}

//把Swap函数放在主函数的后面
void Swap(int* x, int* y)
{
	int z = *x;
	*x = *y;
	*y = z;
}

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

6.1嵌套调用

什么是嵌套调用?在函数中调用其他函数的方式被称为嵌套调用。

void hehe()
{
	printf("hehe");
}

void three_hehe()
{
	hehe();
	hehe();
	hehe();
}

这里举一个简单的例子。hehe函数的作用是打印 “hehe”,three_hehe函数的作用是打印三个 “hehe” ,函数中一共调用了三次hehe函数。three_hehe中调用了 hehe函数。这样就是嵌套调用了。

6.2链式访问

那什么又是链式访问呢?

将一个函数的返回值作为另一个函数的参数。我们再举一个例子

//strlen:求字符串长度,返回值为字符串中字符个数
//方式1
int main()
{
	char a[] = "abcde";
	printf("%d", strlen(a));
	return 0;
}


//方式2
int main()
{
	char a[] = "abcde";
	int ret = 0;
	ret = strlen(a);
	printf("%d", ret);
	return 0;
}

两种方式哪种是链式访问呢?答案是方式1。方式1将 strlen函数的运算结果直接作为printf函数的参数。方式2中,我们通过创建中间变量 ret存储strlen函数的返回值,最后打印 ret的值,是一种间接使用 strlen函数返回值的方式。两种方式各有优缺点,方式 1简洁,代码量少,方式 2虽然代码多,但是对于新手来说可读性高。

7.函数递归

前面我们介绍了函数的嵌套调用(在函数中调用其他函数),递归呢,就是函数自己对自己的嵌套调用,自己调用自己。运用递归我们能将大事化小,用少量代码解决复杂问题。但是函数如果不停地调用自己,我们的程序是会崩溃的,所以我们要有一个终止条件,当函数满足该条件时,停止调用自己,并且我们在每一次调用函数后,更接近这个条件

先来看一个简单的递归:如何递归来求 n 的阶乘?

分析:(假设函数名为fac)当 n为1时,函数直接返回1。n大于1时,函数返回 n * fac(n - 1),直到fac(n - 1)中的(n - 1)为1,函数停止递归。

int fac(int n)
{
	if (n == 1)
	{
		return 1;
	}
	else
	{
		return (n * fac(n - 1));
	}
}

int main()
{
	int n = 0;
	scanf("%d", &n);
	printf("%d", fac(n));
	return 0;
}

 假设我们要求5 的阶乘,画图理解。

红色代表的是函数”递“的部分,程序不断的调用fac函数,一层层往下接近终止条件,绿色代表的是函数”归“的部分,当程序遇到终止条件,会 返回函数值,一层层向上地返回函数值,直到递归的起点。 

我们还能用递归求第 n个斐波那契数。(斐波那契数列:1,1,2,3,5,8,13,21,34,55...从第 3项开始每一项都是前两项的和)

可以这样分析,当 n 小于等于 2时,斐波那契数为1,当 n大于 2时,f(n) = f(n - 1)+ f(n - 2)。当 n大于 2我们就进行递归,每次递归调用函数时 n都减一,直到(n - 2) 与 (n -1)这两个表达式满足终止条件,程序结束'递',开始往上'归'。

int Fib(int x)
{
	if (x <= 2)
	{
		return 1;
	}
	else
	{
		return Fib(x - 1) + Fib(x - 2);
	}
}


int main()
{
	int n = 0;
	scanf("%d", &n);
	printf("%d", Fib(n));
	return 0;
}

最后,由于前几天状态不佳,总结这篇博客时相当敷衍,在进行了删改之后重新发布这篇博客。希望以后能及时调整自己,保持一个学习的状态。

  • 17
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值