C语言 函数

文章目录

前言

在C语言的世界里,函数不仅是代码组织的基本单元,更是程序员构建复杂系统的思维工具。从简单的 main()函数到庞大的库函数集合,函数贯穿了C语言的每个角落。


一、函数的概念

数学中我们其实就⻅过函数的概念,⽐如:⼀次函数 y=kx+b ,k和b都是常数,给⼀个任意的x,就得到⼀个y值。

而在C语言中,函数就是⼦程序, C语⾔中的程序就是⼀个又一个的子程序组合而成。

在项目中,⼀个⼤的计算任务可以分解成若⼲个较⼩的函数(对应较⼩的任务)完成。

并且在C语⾔中我们⼀般会⻅到两类函数:库函数,⾃定义函数


二、函数的分类

2.1 库函数

C语⾔标准中规定了C语⾔的各种语法规则,C语⾔并不提供库函数;C语⾔的国际标准ANSIC规定了⼀ 些常⽤的函数的标准,被称为标准库,那不同的编译器⼚商根据ANSI提供的C语⾔标准就给出了⼀系列函数的实现。这些函数就被称为库函数。

我们前⾯内容中学到的 printf 、 scanf 都是库函数,库函数也是函数,不过这些函数已经是现成的,我们只要学会就能直接使⽤了。有了库函数,⼀些常⻅的功能就不需要程序员⾃⼰实现了,⼀ 定程度提升了效率;同时库函数的质量和执⾏效率上都更有保证。

那我们如何学会库函数的使用呢?下面有库函数的相关网址

库函数相关网址:cplusplus.com - The C++ Resources Network

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

在这个界面中我们可以在上方Search中搜索我们想要认识的库函数,例如 printf 库函数

红色框为函数原型,我们可以从中了解到函数的参数类型,返回类型,这个非常重要,决定了我们要给函数传参的类型以及要用什么类型接收返回值。红色框往下就是这个函数的具体功能,如果看不懂我们可以打开翻译。

而且往下还有示范例子:

并且在左边还能看到这个库函数需要什么头文件

这个浅灰色的就是。

需要注意的是:库函数是在标准库中对应的头⽂件中声明的,所以库函数的使⽤,务必包含对应的头⽂件,不包含是可能会出现⼀些问题的。

库函数包含格式

cpp 复制代码
#include<头文件>

2.2 自定义函数

重要的是自定义函数。
自定义函数和库函数一样,有函数名,返回值类型和函数参数。
所谓自定义就是这些都是我们自己来设计。这给我们自己一个很大的发挥空间。

⾃定义函数和库函数是⼀样的,语法形式如下:

cpp 复制代码
ret_type fun_name(形式参数)
{
	//ret_type是函数返回类型
	//fun_name是函数名
	// ()括号中放的是形式参数
	//{}括起来的是函数体
}

再例如写一个加法函数的格式:

cpp 复制代码
int add(int x,int y) {

	return x + y;
}

三、函数的参数

在函数使⽤的过程中,把函数的参数分为,实参和形参。

看如下代码:

cpp 复制代码
#include<stdio.h>
int add(int x,int y) {

	return x + y;
}

int main()
{
	int a = 3, b = 2;
	int sum = 0;
	sum = add(a, b);
	printf("%d\n", sum);
	return 0;
}

3.1 实参

实际参数就是真实传递给函数的参数。

在上述代码中,调⽤add函数时,传递给函数的参数a和b,称为实际参数,简称实参。

cpp 复制代码
​
#include<stdio.h>
int add(int x,int y) {

	return x + y;
}

int main()
{
	int a = 3, b = 2;
	int sum = 0;
	sum = add(a, b);   //a和b为实参
	printf("%d\n", sum);
	return 0;
}

​

3.2 形参

定义函数 add 的时候,在函数名 add 后的括号中写的 x 和 y ,称为形式参数,简称形参。

为什么叫形式参数呢?实际上,如果只是定义了 add函数,⽽不去调⽤的话,add函数的参数 x和y只是形式上存在的,不会向内存申请空间,不会真实存在的,所以叫形式参数。形式参数只有在 函数被调⽤的过程中为了存放实参传递过来的值,才向内存申请空间,这个过程就是形式的实例化。

cpp 复制代码
​
​
#include<stdio.h>
int add(int x,int y) {   //int x, int y 为形参

	return x + y;
}

int main()
{
	int a = 3, b = 2;
	int sum = 0;
	sum = add(a, b);   //a和b为实参
	printf("%d\n", sum);
	return 0;
}

​

​

3.3 形参和实参的关系

实参是传递给形参的,他们之间是有联系的,但是形参和实参各⾃是独⽴的内存空间。

这个现象是可以通过调试来观察的。请看下⾯的代码和调试演⽰:

cpp 复制代码
#include<stdio.h>
int add(int x,int y) {

	int sum = 0;
	sum = x + y;
	return sum;
}

int main()
{
	int a = 3, b = 2;
	int sum = 0;
	sum = add(a, b);
	printf("%d\n", sum);
	return 0;
}

我们在调试的可以观察到,x和y确实得到了a和b的值,但是x和y的地址和a和b的地址是不⼀样的,所以我们可以理解为形参是实参的⼀份临时拷⻉。


四、函数的调用

4.1 传值调用

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

例如以下交换函数代码:

cpp 复制代码
#include <stdio.h>
void Swap1(int x, int y)
{
	int tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	Swap1(a, b);
	printf("交换前:a = % d b = % d\n", a, b);
	printf("交换后:a = % d b = % d\n", a, b);
	return 0;
}

我们发现其实没产⽣交换的效果,这是为什么呢? 调试⼀下

我们发现在main函数内部,创建了a和b,a的地址是0x000000acfd18fc74,b的地址是0x000000acfd18fc94, 在调⽤ Swap1函数时,将a和b传递给了Swap1函数,在Swap1函数内部创建了形参x和y接收a和b的值,但是**x的地址是0x000000acfd18fc50,y的地址0x000000acfd18fc58,**x和y确实接收到了a和b的值,不过x的地址和a的地址不 ⼀样,y的地址和b的地址不⼀样,相当于x和y是独⽴的空间,那么在Swap1函数内部交换x和y的值, ⾃然不会影响a和b,当Swap1函数调⽤结束后回到main函数,a和b的没法交换。Swap1函数在使⽤的时候,是把变量本⾝直接传递给了函数,这种调⽤函数的⽅式我们之前在函数的时候就知道了,这种叫传值调⽤。

结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实 参。所以Swap是失败的了。

4.2 传址调用

那我们应该怎么修改才可以让Swap函数达到我们想要实现的功能呢?

那这就需要我们的传址调用了,使⽤指针了,在main函数中将a和b的地址传递给Swap函数,Swap 函数⾥边通过地址间接的操作main函数中的a和b,并达到交换的效果就好了。

代码如下:

cpp 复制代码
#include <stdio.h>
void Swap(int* px, int* py)
{
	int tmp = 0;
	tmp = *px;
	*px = *py;
	*py = tmp;
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	printf("交换前:a = % d b = % d\n", a, b);
	Swap(&a, &b);
	printf("交换后:a = % d b = % d\n", a, b);
		return 0;
}

调试一下看看结果:

我们发现现在a和px的地址,b和py的地址就相同了,那么就可以交换地址以达到交换值的效果了,这就是传址调用。


五、函数的声明和定义

5.1 函数的定义

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

如下,下面整个代码都是add函数的定义。

cpp 复制代码
int add(int x, int y) {

	int sum = 0;
	sum = x + y;
	return sum;
}

我们已经定义好了add函数,那我们如何调用它才不会出错呢?

这就需要我们函数的声明了。

5.2 函数声明

函数声明就是告诉编译器函数的接口信息,让编译器提前知道有这个函数存在。

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

特别注意的是:函数定义在调用这个函数前的话可以当做特殊声明,如下:

cpp 复制代码
#include <stdio.h>
int add(int x, int y) {

	int sum = 0;
	sum = x + y;
	return sum;
}
int main()
{
	int a = 3, b = 2;
	int sum = 0;
	sum = add(a, b);
	printf("%d\n", sum);
	return 0;
}

我们也可以提前声明add函数让编译器知道有这个函数存在。

cpp 复制代码
#include <stdio.h>
int add(int x, int y);    //函数声明
int main()
{
	int a = 3, b = 2;
	int sum = 0;
	sum = add(a, b);
	printf("%d\n", sum);
	return 0;
}

int add(int x, int y) {    //函数定义

	int sum = 0;              
	sum = x + y;
	return sum;
}

六、嵌套调用和链式访问

6.1 嵌套调用

嵌套调⽤就是函数之间的互相调⽤。

如下代码:

cpp 复制代码
void fun() {
	printf("Hello\n");
	funn();
	return;

}
void funn() {
	printf("你好\n");
	return;

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

main函数调用fun函数打印 Hello ,fun函数又调用funn函数打印 你好 。

这就是函数的嵌套调用。

6.2 链式访问

所谓链式访问就是将⼀个函数的返回值作为另外⼀个函数的参数,像链条⼀样将函数串起来就是函数的链式访问。

⽐如:

cpp 复制代码
#include <stdio.h>
#include<string.h>
int main()
{
	int len = strlen("abcdef");//1.strlen求⼀个字符串的⻓度

		printf("%d\n", len);//2.打印⻓度

		return 0;
}

前⾯的代码完成动作写了2条语句,把如果把strlen的返回值直接作为printf函数的参数呢?这样就是⼀ 个链式访问的例⼦了。

cpp 复制代码
#include <stdio.h>
#include<string.h>
int main()
{
	printf("%d\n", strlen("abcdef"));//链式访问

		return 0;
}

七、函数递归

7.1 递归的概念与思想

在C语⾔中,函数递归就是函数⾃⼰调⽤⾃⼰。

递归的思想:把⼀个⼤型复杂问题层层转化为⼀个与原问题相似,但规模较⼩的⼦问题来求解;直到⼦问题不能再 被拆分,递归就结束了。所以递归的思考⽅式就是把⼤事化⼩的过程。

递归中的递就是递推的意思,归就是回归的意思,接下来慢慢来体会。

7.2 递归的限制条件

递归在书写的时候,有2个必要条件:

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

每次递归调⽤之后越来越接近这个限制条件。

7.3 递归的例子

求n的阶乘

代码如下:

cpp 复制代码
#include <stdio.h>
int Fact(int n)
{
	if (n <= 0)
		return 1;
	else
		return n * Fact(n - 1);
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	int ret = Fact(n);
	printf("%d\n", ret);
	return 0;
}

我们输入5,得到的结果为120

画图演示一下递归的流程:

相关推荐
大海里的番茄1 小时前
让操作系统的远程管理更简单用openEuler+cpolar
linux·c语言·c++
小龙报1 小时前
VS2022调试技巧 + 实战案例
android·服务器·c语言·数据库·c++·windows·visual studio
枫叶丹41 小时前
【Qt开发】Qt窗口(四) -> QDockWidget浮动窗口
c语言·开发语言·c++·qt·开源
kkkkkkkkk_12011 小时前
【强化学习】周博磊强化学习纲要学习笔记——第二课上
笔记·学习
秋深枫叶红1 小时前
嵌入式第二十五篇——数据结构单向链表
c语言·数据结构·学习·算法
碧海潮生_CC1 小时前
【CUDA笔记】05 使用 AMGX 实现泊松图像编辑
笔记·cuda
烛衔溟1 小时前
C语言动态规划:最长公共子序列深度解析
c语言·数学建模·动态规划·算法优化·最长公共子序列·lcs
烛衔溟1 小时前
C语言动态规划:0-1背包问题深度解析
c语言·数学建模·动态规划·算法优化·0-1背包问题
摇滚侠2 小时前
零基础小白自学 Git_Github 教程,Idea 中使用 Git 进阶,笔记17
笔记·git·github