c语言函数详解

      各位看官好,这是鄙人的第六篇博客了,今天讲的是c语言里面的函数,函数在c语言中是基本单位,在代码运行起来起着至关重要的作用。

函数的定义与分类:

定义

       在C语言中,函数是指一组执行特定任务的语句,这些语句可以重复使用,并且可以在程序的不同部分调用。通过使用函数,程序员可以将程序分解成小而独立的部分,从而使代码更加清晰、易于维护和修改。函数定义包括以下几个部分:

返回类型 函数名(参数列表)

{

 函数体;

}

       其中,返回类型指定了函数返回值的数据类型;函数名是一个标识符,用于标识该函数;参数列表是一组输入参数,用于传递数据给函数;函数体是一组执行特定任务的语句。C语言中支持多种不同类型的返回值。常见的数据类型包括int、float、double、char等。如果一个函数不需要返回值,则可以将返回类型设置为void。return的意思就是返回,当你需要返回值的时候就在末尾写出这个,后面加要返回的数值或者字符。

       在C语言中,每个函数都必须有一个唯一的名称。通常情况下,函数名称应该具有描述性,并且应该与所执行任务相关联。当然也可以随便取,但是不能与指定名重合,如,int,char,float等。肯定不能常见一个函数如:

bcdefd560e7348919833d344a0ba847f.png5a8deb28584849ba9d218440ebba4539.png

分类

依据出处:1:库函数:c语言内部提供的函数,如printf就是c语言内部提供的函数。当然你要使用库函数,不能直接用呀,一定要向系统要啊,所以使用库函数一定要引入对应的头文件。2:自定义函数,从名字上看就知道,自己定义的函数,也许库函数里面有这个函数达到这个效果,但是我们可以直接写一段代码,也达到这个效果,也可以比这个效果更好,这就是自定义函数的好处,更灵活,库函数的作用是已经被定好的,不能更改的了。

依据主次:这个是我认为也可以区分的,当然这是个人的意见,按出处是更有认可度的函数分类。1:主函数,我们都知道,写代码的时候一定会先写一个int main,然后在大括号里面写代码。并且main只能又一个,其他的库函数都可以写很多次,就是名字不能随便用嘛,但main什么都不行,只能有一个,所以我认为main可以作为主函数单独写出来。2:子函数,这就是我们自己写的函数了,其实和自定义函数一个意思,只是与前面这个主函数相提并论的话,这就是子函数,不在主函数里面。一个单独函数,但是没有主函数子函数也运行不了。

库函数

       在上一个小节我们有提过,在使用一些c语言内部提供的函数。这就是库函数。而且库函数的作用也很大,比如说我们平常使用的printf(打印),pow(计算x的y次方)等。

       那库函数怎么使用,库函数有哪些嘞。这不用我们专门去记,因为在c语言的官方网站是为我们准备好的,只需要我们去查就可以了。当然因为c语言是由国外传到中国的,网站当然是英文的,所以使用前记得翻译。

dc35a0eef1a7469ebb7761e7e66e73e8.png

自定义函数

        自定义函数由程序员自主设计,和普通的函数一样有函数名、返回类型、形式参数等。可以自己确定返回类型,和函数名。

返回类型 函数名(参数列表)

{

 函数体;

}

示例1:输入数值判断是否能被5整除

void wuwu()
{

	int b = 0;
	do
	{
		int a = 0;
		printf("请输入被除数");
		scanf("%d", &a);
		while (a > 100001 || a < 1)
		{
			printf("输入错误,请重新输入");
			scanf("%d", &a);
			if (a <= 100000 && a >= 1)
			{
				break;
			}
		}
		if (a % 5 == 0)
		{
			printf("yes\n");
			//break;
		}
		else
		{
			printf("no\n");
			//break;
		}
		printf("0:退出   else:继续");
		scanf("%d", &b);
	} while (b);
}
int main()
{
        wuwu();
	return 0;
}

       大家可以看到,我在这里写了int main和void wuwu两个函数,那么我就就称int main为主函数,而void wuwu则是子函数,因为如果没有int main的话,void wuwu无论写的再好,都是不会被编译的。当然这两个函数都是自定义函数。

示例2:错误示范

void wuwu(int arr, int c)
{
	int i, j, dlh;
	for (i = 0; i < c - 1; i++)
	{
		int yh = 0;
		for (j = 0; j < c - 1 - i; j++)
		{
			if (arr[j]>arr[j + 1])
			{
				dlh = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = dlh;
				yh = 1;
			}
		}
		if (yh == 0)
		{
			break;
		}
	}
}

int main()
{
	int arr[] = { 1, 8, 9, 3, 88, 77, 0, 69 };
	int c = sizeof(arr) / sizeof(arr[0]);
	int dd = 0;
	wuwu(arr, c);
	for (dd = 0; dd < c; dd++)
	{
		printf("%d\n", arr[dd]);
	}

	return 0;
}

 

        这是一个冒泡排序,排序结果是由小到大的,但这里的错误是,传参,就是从主函数传到子函数的参数。这里就不过多的赘述,一会我会在下面详细的简绍的。现在大家可以先思考这个代码传递参数那里有问题。

函数参数

简单来说,形式参数(形参)就是形式上的参数,没有确定值。
而实际参数(实参)是实际存在的,已经确定的参数,常量,变量,表达式,都是实参。

实参

358c102a06b3453f9a73077918c45c7e.png         这个上面我举了一个很简单的例子。函数被赋了确定的值,则为实参,全称为实际参数。

形参

2b234d8574794c448e5b1764aacd04a7.png

实参与形参的区别

cdf251a536b34b38a51356815337b9a9.png       可以大体理解为以下几点:1:实参赋值后,会占内存,但形参只是一份临时拷贝,在使用的时候会有,调用结束后,就会不见。2:没有实参,那就没有形参,实参是形参的前提。3:生命周期,形参离开了被调用函数则会找不到位置,但实参出了,被调用函数还是可以使用。4:如果传递给形参的实参改变,那么形参也会跟着改变。5:如果实参是数组名,那么形参传递的之就是地址的值。

函数调用(传值和传址)

传值

726e21e1f4444190a0afd09dd0a91572.png

       这就是一个简单的函数调用,且为传值。这里将a和b的值属性传到子函数wuwu里面,并且在调用函数wuwu里面进行相加,然后return返回,由c接收 。所以这就是调用函数中的传值。

传址

66a377f4500f4d7ab6a4ea7488c35621.png

       以上为传址,大家可以看到子函数wuwu并没有返回值,但是我们输入a为2b为6,但是经过wuwu后,a和b的值却交换了。虽然wuwu里面是有交换数值的操作,但是为什么会交换呢?这是因为&,取地址符号,在给wuwu传递参数的时候,我们将a和b的地址传给了它,那么在wuwu里面如果改变a与b的带函数那么实参s与b也会发生相应的改变。 

总结:传值无法改变实际参数,传址可以改变传递参数的内容。

错误揭秘:大家看过传值与传址后,应该想到了,为什么上面那个代码是有问题的了吧。因为我传递过去的arr是一个数组,但是我在wuwu的接收的传递参数的时候却写的是一个整数。因为接收类型的错误那么这个子函数也是会报错的。所以调用函数有以下几点是很重要的。1:调用函数的名字一定要写正确,可以取任意名字(最好是有特殊意义,方便记住和他人观看)。不能主函数写一个名字,子函数也是另外一个名字,那么系统是找不到,应该使用的子函数的。2:接收类型,主函数传递的参数类型与子函数必须一模一样,不然也是会报错的。3:返回类型的正确,int类型就一定要返回一个整数型,其他类型也是一样的,如果不返回,那么就要在函数名前标明void(无返回)。

函数嵌套与链式访问

函数嵌套

d98820bdb0004c1795835e1fab76c305.png

       这应该是每一个码农在开始学习c语言的时候,会看到的代码吧。这就是一个函数嵌套 ,我们先调用了main函数,然后在main函数的内部又调用了printf函数,这就是嵌套调用,注意不能嵌套定义。

链式访问

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

        直接把一个函数的返回值作为另一个函数的参数,这就是链式访问,注意链式访问,尽量少写,因为如果写的太多层的话,会将自己或者其他人绕进去,写的话,写一层两层就够了。太多编程员自己都有可能会忘记结果。像下面这个,对于新手来说就有一点点的不友好了。

#include<stdio.h>
int main()
{
    printf("%d", printf("%d", printf("%d", 43)));
    return 0;
}

代码简介:1->printf这个函数的返回值是它打印字符的个数,首先进入最外层的printf函数.2->这层函数需要第二层函数printf("%d", printf("%d", 43))的返回值3->而第二层的printf函数又需要第三层函printf("%d", 43)的返回值4->在执行完第三层的printf("%d", 43)函数后,返回打印字符的个数2,printf("%d", printf("%d", 2))4->第二层得到返回值2,打印2,而此时第二层函数也返回它打出的字符的个数1printf("%d", 1)5->最后打印1,也就形成了4321的输出结果。

函数定义方法

       在文章的开头我们就已经讲过函数的定义,后面也只是讲过不能重命名,不能写错名字和传参的数据类型。但是却没有明确的写出函数嵌套定义是什么样子的,那么下面我们就来看一个比较简单的嵌套定义。

ca3562ff2f624771bb8377a7894e4a3b.png

       这里就很明确的表示出了,嵌套定义是什么样子的,就是在一个函数里面定义另外一个函数。但这里vs编译器很贴心的下出了,认为int xixi应该是一段语句,所以提醒我们加上“;”号,如果加上的话int xixi就会变成一条语句,那就避免了嵌套定义的情况了

dff22e1511534700b8911144ca6838d0.png

          这样就是正确的了,想在定义一个函数的话,就再写一个新的定义函数,记住不能重命名。

函数声明

         当您在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的。在这种情况下,您应该在调用函数的文件顶部声明函数。如果使用用户自己定义的函数,而该函数与调用它的函数(即主调函数)不在同一文件中,或者函数定义的位置在主调函数之后,则必须在调用此函数之前对被调用的函数作声明。所谓函数声明,就是在函数尚在未定义的情况下,事先将该函数的有关信息通知编译系统,相当于告诉编译器,函数在后面定义,以便使编译能正常进行。

  • 函数声明——头文件.h
  • 函数定义——函数实现的源文件.c

每一个函数都可以分成这两个文件编写,也可以几个函数写在两个文件中

63a7f1ce810f4de7aff341bba9243615.png

e541f1d824a2474bb8e47175b997196f.png

注意:一个函数只能被定义一次,但可以声明多次。

函数递归

        程序调用自身的编程技巧称为递归( recursion)。表示一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。总而言之就是,自己调用自己,并且每调用一次,代码的计算量就会变少一点,直到达到定义的递归次数截止。

8e5e49d365da4a548679f60f3d6e0b1b.png

       我们如果想要得到一个数字的每一位,那就%10,那么就可以得到最后的一位,然后我们在把最后一位去掉,一直到整个数值成为一个一位数,然后停止下来,然后在最后写一个printf,依次打印,那么就达到了效果。

函数迭代

        迭代实际上就是重复,可以简单一点理解就是循环。当我们使用迭代时,循环不需要大量调用函数,重复的计算会少很多,这个程序的运行速度会加快不少,只是这个程序的代码量会大很多。

     关于递归与迭代的区别肯定会有一个经典案例斐波那契数列,我们可以看一下递归与迭代的代码量和使用方法的区别。

9f9f69177aef4ef3850838d763763d40.png

递归

fa6ce6a7342c4b7cb6e3976646736289.png

迭代

        以上就是鄙人这次想与大家分享的全部内容了,当然还有很多不足之处,希望各位看官不吝赐教,在评论区写下来,鄙人以此改正。 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值