函数传参与递归概述

以个人理解为主体的C语言学习理解梳理记录,注重于更好的理解一些概念,肯定有说的不太对的地方,还望在包涵的时候帮我指出一下错误之处!感激不尽!

目录

 函数定义的类型

函数的使用与传参

 递归概


初学C语言的时候,我们通常将我们需要运用编程所解决的问题写在main函数中,但是全部屯在main函数里不仅会使得代码变得冗长,还会因为过于集中影响我们对代码逻辑的梳理也不易阅读,所以对一些单一功能的实现我们可以专门定义一个函数来实现。


就好像我们想制批量作可乐,我们有了绝大部分的生产机器也有生产物资,可是缺少了关键的糖浆,那么我们可以找一家加工厂,让他们帮我们专门生产糖浆。函数就好像一家专业对口的加工厂,我们可以让其实现特定功能而不用全部堆在main函数里头。


 函数定义的类型

那么该怎么定义一个函数,我们又该怎么去使用它呢?

首先函数的声明要在main之前

可以在头文件里面声明与定义,在源文件里应用

函数的声明有两种形式  

1.不返回值 

void function()

 这样声明的函数是没有返回值的,所以在声明的时候在函数名称前加上void即可。

2.返回类型值

int function()
{
     return 0;
}

当声明的一个函数需要返回值的时候,我们需要确定这个返回值的类型是什么,如上,函数返回了一个0,那么我们声明时需要指定函数的返回值类型是整形。


注意!函数返回值的时候只能返回一个,我们可能会想到能不能写下return 2,3来返回两个值,很显然,逗号表达式只生效于最后一个参数没法返回两个值,并且函数本身也不支持其他形式的返回多个值。


函数的使用与传参

了解了函数的声明方式后就是使用的方法了,我们先借由函数实现一个简单的功能,比如简单的两个变量交换值。

于main函数中,这项功能非常容易实现,无非是交换一瓶可乐和芬达的事情,但是直接倒进去是完全不行的,这简单,拿个空瓶就好。

int main()
{
	int coke = 1;
	int fanda = 2;
	int bottle = 0;

	bottle = coke;//把可乐倒进空瓶
	coke = fanda;//芬达倒进已经空了的可乐瓶
	fanda = bottle;//把瓶子里的可乐倒进芬达瓶子

	printf("%d %d", coke, fanda);

	return 0;
}

 芬达就成功的灌倒可乐瓶里了。

那么回到函数上来,这很简单,如法炮制就好了。

我们先创建一个函数,称为exchange()

void exchange()//错误示范
{
	bottle = coke;
	coke = fanda;
	fanda = bottle;
}

这样写是不对的,因为根本没有参数传进去!就好像工厂没有进料口一样,所以我们要在括号内声明我们要丢进这个加工厂的“原料类型”是什么

coke的变量类型是整形,那么好办,我们就把“进料口”也设置为int

void exchange(int coke ,int fanda)//形参的名字不必一样,随意取名也可以。
{
	int bottle = 0;

	bottle = coke;
	coke = fanda;
	fanda = bottle;
}

接下来是main函数里调用exchange函数

int main()
{
	int coke = 1;
	int fanda = 2;
	int bottle = 0;

	exchange(coke,fanda);

	printf("%d %d", coke, fanda);

	return 0;
}

 运行结果如下:

 奇怪的是,我们的加工厂仿佛做了无用功一样,完全没有交换我们的饮料,这是为什么?

形参与实参

原因其实很简单,在exchange()内被交换参数的其实并不是main函数里的实参coke和fanda,而是形式参数coke和fanda。

形式参数:当实参传递给形参的时候,形参是一份临时拷贝,而存放这份拷贝的,就是“进料口”int coke

简单来讲,在exchange里被交换的,只是一份拷贝而已,真正所需要被交换的参数还在main里面,拷贝被交换了,本体自然是没有受到任何影响。

那这样子的函数根本没有用啊!我们该怎么让它去改变我们的实参来达成我们所需要的功能呢?

既然函数拿的是传入实参的一份拷贝,那么我们不如传给函数一个可以直接改变实参的东西:实参的地址。


在程序运行的时候,每次声明一个变量都会在栈区为这个变量开辟一段内存空间,就好像开启了存储仓库一样以便于存放数据,但是开了仓库不知道东西放在哪个仓库里就很尴尬了,所以同样的,编译器会为变量生成对应的地址,以方便访问,可以理解为这个变量的门牌号。

所以当我们把实参的地址传过去时,函数就可以顺着地址找到实参本人,运算的就是地址所指向的实参了!

void exchange(int*pcoke ,int*pfanda)//接收传参的时候,传过来的地址用指针变量接收
{
	int bottle = 0;

	bottle = *pcoke;//由于指针变量所指向的变量才是真正的实参,此处应继续使用指针
	*pcoke = *pfanda;
	*pfanda = bottle;
}

int main()
{
	int coke = 1;
	int fanda = 2;
	int bottle = 0;

	exchange(&coke,&fanda);//在传参的时候取实参的地址,使用取地址操作符&

	printf("%d %d", coke, fanda);

	return 0;
}

运行结果如下


总结一下,以上使用了两种函数的调用方式:1.传值调用 2.传址调用

调用:函数的形参和实参分别占有不同的内存块,形参的改变不会改变实参

调用:将函数与函数外的变量建立真正联系,使得变量可以真正的改变,也就是函数内部可以改变函数外部的变量。


所以有个小问题:此时若是需要声明函数帮助运算,什么时候要用指针什么时候不用呢?

当需要改变要传进函数里的数据时,应该使用指针

当只是需要函数帮忙计算值,不需要改变传进去的函数时就不需要指针


 数组传参的时候,只是传过去了首元素的地址,防止空间的浪费。就不需要很大的一份数组拷贝了,我们将会在之后讨论数组的时候来讲述数组的函数传参。

 递归概述

 递归是什么?

递归,指的是程序直接或间接调用自身的编程技巧,只需要少量的程序就可以完成一些大量多次重复计算。

在正式介绍递归前,先罗列两个使用递归时最基本的原则。

1.递归一定要有限制条件,使得递归停止

2.每次递归都一定要让整个函数向限制条件靠近


 那我们就来看看递归是怎么运作的

 有如下问题:

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

也就是输入1234,打印1 2 3 4


无符号:unsigned 通常指定整形,在unsigned定义下的整形只能是正数,这是因为用于存储int类型变量是否为正负的bit也被用来存放变量数据了,也正因如此,用unsigned定义下的整型变量也可以容下更大的数值。


基本思路如下:

1234除以10得到123,1234%10得到个位4

那么我们让输入的数字每除以10就模10,以得到每一位数。

程序如下:

void print(unsigned int n)
{
	if (n > 9)
	{
		print(n / 10);
	}
	printf("%d ", n % 10);
}

int main()
{
	unsigned int num = 0;
	scanf("%u", &num);
	print(num);

	return 0;
}

 程序运行结果如下

在print函数中,每一次运行都会再次调用一次print函数自己,每一次传进去的参数都是n/10,并且在n>9也就是只剩一位数的时候不在进入print函数自身 ,在递归结束后,再执行打印程序,每一次打印都是当前n10模10所取的个位数。

乍一看可能难以理解,但是复杂的问题可以通过拆解来简化,我们可以尝试画图来了解

 没有选择嵌字,字迹有些潦草还望见谅。

由此,我们可以看到,递归的基本运行逻辑,这玩意需要一些巧劲,刚刚接触可能难以理解,还是需要多尝试实现才可以更好的掌握这种编程方法,其中也有不少更多的递归方法,比如return一个表达式来实现递归。

具体的题目在此不做细述。

 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值