【C语言初阶】函数详解

目录

一.在C语言中什么是函数?

二.为什么需要函数?

三.函数的分类:

1.库函数:

 2.自定义函数:

 四.函数的返回类型:

五.参数类型:

1.形式参数:

2.实际参数:

形参和实参的区别和联系:

六.调用:

1.传值调用:

2.传址调用:

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

1.函数嵌套调用:

2.链式访问:

 八.函数的定义与声明:

九.函数的递归:

一.在C语言中什么是函数?

第一,函数就是 C 语言的模块,一块一块的,有较强的独立性,可以相互调用,换句话说,C 语言中,一个函数里面可以调用 n 个函数,即大函数调用小函数,小函数又调用“小小”函数。这就是结构化程序设计,所以面向过程的语言又叫结构化语言

第二,函数就是一系列 C 语句的集合,能完成某个特定的功能。需要该功能的时候直接调用该函数即可,不用每次都堆叠代码。需要修改该功能时,也只需要修改维护这一个函数即可。

二.为什么需要函数?

第一,将语句集合成函数的好处是方便代码重用。所谓“重用”,是指有一些代码的功能是相同的,操作是一样的,只不过针对的数据不一样,这时就可以将这种功能写成一个函数模块,以后用到这个功能时只需要调用这个函数模块就可以了,不需要再重复地编写同样的代码。这样可以解决大量同类型的问题,避免重复性操作

第二,将语句集合成函数方便代码的维护。哪个功能出问题了,或者需要修改某个功能,那就只需要修改某个功能的函数就可以了。
所以,函数有利于程序的模块化。这实际上就是面向过程的思想。面向过程语言最基本的单位不是语句,而是函数。

三.函数的分类:

1.库函数:

库函数的定义:C语言的库函数并不是C语言本身的一部分,它是由编译程序根据一般用户的需要编制并提供用户使用的一组程序。 C的库函数极大地方便了用户,同时也补充了C语言本身的不足。 事实上,在编写C语言程序时,应当尽可能多地使用库函数,这样既可以提高程序的运行效率,又可以提高编程的质量。

当我们需要使用输入,输出函数时,也就是scanf和printf函数,我们都是在代码开头包含头文件include<stdio.h>,像这样stdio.h这种就是库函数,像这样的库函数还有很多很多。这里给大家提供一个软件MSDN和一个网站https://en.cppreference.com/w/,大家可以去学习一下。

 2.自定义函数:

库函数都是C语言直接有的,而程序员主要就是写自定义函数,来解决各种复杂的问题。

dataType  functionName(){
    //body
}
  • dataType 是返回值类型,它可以是C语言中的任意数据类型,例如 int、float、char 等。
  • functionName 是函数名,它是标识符的一种,命名规则和标识符相同。函数名后面的括号( )不能少。
  • body 是函数体,它是函数需要执行的代码,是函数的主体部分。即使只有一个语句,函数体也要由{ }包围。
  • 如果有返回值,在函数体中使用 return 语句返回。return 出来的数据的类型要和 dataType 一样。

我们可以写一个函数来求两个数中的最大值:

int get_max(int x, int y)
{
	if (x > y)
		return x;//大括号内部也可以直接写成 return x>y?x:y
	else          //三目操作符,如果x>y为真,返回x,否则返回y
		return y;
}
#include<stdio.h>
int main()
{
	int a = 10;
	int b = 5;
	int c = get_max(a, b);
	printf("最大值为%d\n", c);
	return  0;
}

 四.函数的返回类型:

1.函数返回值与return语句

函数的返回值,是通过函数中的return语句来获得的。

(1)return语句的一般格式:  return ( 返回值表达式 );  

(2)return语句的功能:返回调用函数,并将“返回值表达式”的值带给调用函数。

注意:被调用函数中无return语句,并不是不返回一个值,而是一个不确定的值。为了明确表示不返回值,可以将函数类型定义为“void”,表示为“无(空)类型”。

2.函数类型

在定义函数时,对函数类型的说明,应与return语句中返回值表达式的类型一致,也就是说函数的类型是函数返回值的类型,它可以是我们已经学习过的int、char、floar、double中的任意类型,也可以是我们要在后面学习的构造数据类型和指针类型。如果不一致,则以函数类型为准。如果缺省函数类型,则系统一律按整型处理。

有返回值:

int Add(int x, int y)
{
	return x + y;//求两个数的和,需要返回和的值
}               
#include<stdio.h>
int main()
{
	int a = 10;
	int b = 5;
	int c = Add(a, b);
	printf("最大值为%d\n", c);
	return  0;
}

无返回值:


void hello()
{
	printf("Hi\n");//只需要打印,不需要返回什么
}
#include<stdio.h>
int main()
{
	hello();
	return 0;
}

​

五.参数类型:

1.形式参数:

在函数定义中出现的参数可以看做是一个占位符,它没有数据,只能等到函数被调用时接收传递进来的数据,所以称为形式参数,简称形参

int Add(int x, int y)//这里的x,y就是形参
{
	return x + y;
}               

2.实际参数:

函数被调用时给出的参数包含了实实在在的数据,会被函数内部的代码使用,所以称为实际参数,简称实参

int main()
{
	int a = 10;
	int b = 5;
	int c = Add(a, b);//a,b就是实参
	printf("最大值为%d\n", c);
	return  0;
}

形参和实参的区别和联系:

1) 形参变量只有在函数被调用时才会分配内存,调用结束后,立刻释放内存,所以形参变量只有在函数内部有效,不能在函数外部使用

2) 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的数据,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参,所以应该提前用赋值、输入等办法使实参获得确定值。

3) 实参和形参在数量上、类型上、顺序上必须严格一致,否则会发生“类型不匹配”的错误。当然,如果能够进行自动类型转换,或者进行了强制类型转换,那么实参类型也可以不同于形参类型。

4) 函数调用中发生的数据传递是单向的,只能把实参的值传递给形参,而不能把形参的值反向地传递给实参;换句话说,一旦完成数据的传递,实参和形参就再也没有瓜葛了,所以,在函数调用过程中,形参的值发生改变并不会影响实参

实例验证:当我们写一个函数来交换两个数:

void Swap(int x, int y)
{
	int temp = x;
	x = y;
	y = temp;
}
#include<stdio.h>
int main()
{
	int a = 5;
	int b = 9;
	printf("交换前:a=%d,b=%d\n", a, b);
	Swap(a, b);
	printf("交换后:a=%d,b=%d\n", a, b);
	return 0;
}

 咦?为什么交换前后没有变呢?我们不妨把他们的地址调试出来看看就知道了。

 总结:虽然说x,y的值被交换了,但x,y和a,b的地址是不一样的,即形参和实参的地址是不一样的,形参只是实参的一份临时拷贝,改变形参的值不会改变实参。

六.调用:

1.传值调用:

传值调用是指方法在调用参数时,不是对原参数进行操作,而是创建参数的拷贝并对其进行操作,这种调用有利于保护数据.

如同上述的求两个数的最大值:只需要实参把值传给形参就是,不需要改变什么。

int get_max(int x, int y)
{
	if (x > y)
		return x;//大括号内部也可以直接写成 return x>y?x:y
	else          //三目操作符,如果x>y为真,返回x,否则返回y
		return y;
}
#include<stdio.h>
int main()
{
	int a = 10;
	int b = 5;
	int c = get_max(a, b);
	printf("最大值为%d\n", c);
	return  0;
}

2.传址调用:

传址调用:传址就是将参数的地址传给函数进行调用。

还是用刚才的Swap函数的例子:

void Swap(int *pa, int *pb)
{
	int temp = *pa;//这里*为解应用,即a,b的值
	*pa = *pb;
	*pb = temp;
}
#include<stdio.h>
int main()
{
	int a = 5;
	int b = 9;
	printf("交换前:a=%d,b=%d\n", a, b);
	Swap(&a,&b);//&为取地址
	printf("交换后:a=%d,b=%d\n", a, b);
	return 0;
}

 结果:

传址调用的原理:

首先把a,b的地址传过去使用符号&,然后用指针接收

Swap(&a,&b);//&为取地址
void Swap(int *pa, int *pb)
{
	int temp = *pa;//这里*为解应用,即a,b的值
	*pa = *pb;
	*pb = temp;
}

总结:传址调用是将地址作为参数,实际参数的地址和指针的地址是一样的,但将指针所指向地址的值进行了改变,当退出函数时,pa和pb两个变量还是会被销毁,但是我们已经将这块地址的值改变了,所以实际参数的值也会被改变,因为他们的地址是相同的

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

1.函数嵌套调用:

函数的嵌套调用是指在一个C语言函数里面再执行另一个函数,这样通常称为函数的嵌套调用。

void print_three()
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("hello\n");
	}
}
void print_line()
{
	print_three();
}
#include<stdio.h>
int main()
{
	print_line();

	return 0;
}

函数里面可以一直嵌套函数,但不能在函数里面在定义一个函数,即函数可以嵌套调用,不能嵌套定义。如:

void print_line()
{
	void print_three();//这样就是嵌套定义,就不行
}

2.链式访问:

链式访问:把一个函数的返回值作为另一个函数的参数

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

 为什么打印出来是4321呢?这就得了解printf的返回值了?

 这里可知,printf的返回值是字符的个数,上述,先打印43,而43是两个字符,然后下一个printf打印2,2又是一个字符,最后一个字符就打印1。所以最后打印出来就是4321。

 八.函数的定义与声明:

我们还是以上述求两个数的最大值为例子。

1.第一种:

int get_max(int x, int y)//这里就是函数的定义
{
	if (x > y)
		return x;//大括号内部也可以直接写成 return x>y?x:y
	else          //三目操作符,如果x>y为真,返回x,否则返回y
		return y;
}
#include<stdio.h>
int main()
{
	int a = 10;
	int b = 5;
	int c = get_max(a, b);//函数的调用
	printf("最大值为%d\n", c);
	return  0;
}

2.第二种:

int get_max(int x, int y);//函数的声明,这里的x,y可以省略写成
#include<stdio.h>       //int get_max(int,int)
int main()
{
	int a = 10;
	int b = 5;
	int c = get_max(a, b);//函数的调用
	printf("最大值为%d\n", c);
	return  0;
}
int get_max(int x, int y)//函数的定义
{
	if (x > y)
		return x;//大括号内部也可以直接写成 return x>y?x:y
	else          //三目操作符,如果x>y为真,返回x,否则返回y
		return y;
}

但是我们通常写成第一种,更加简洁明了。

还有一种就是我们在做很大的项目时,我们需要写很多代码时,而且需要很多人一起共同完成,我们就需要把代码分成很多板块,如同上述求两个数中的最大值:

 

 具体写法请移看三子棋具体写法,如何分为三个板块。

九.函数的递归:

递归:一个函数在它的函数体内调用它自身称为 递归调用,这种函数称为递归函数 。 执行递归函数将反复调用其自身,每调用一次就进入新的一层,当最内层的函数执行完毕后,再一层一层地由里到外退出。

可能这样不好理解,那我们用具体的题来实验一下,如果我们要打印一个四位数的每一位,大家会怎么做呢?比如将1234的每一位打印在屏幕上,这个时候我们就可以用递归很好的解决这个问题

代码如下:

#include<stdio.h>
void print(int n)
{
	if (n > 9)
		print(n / 10);
	printf("%d ", n % 10);
}
int main()
{
	int n = 0;
	scanf("%d", &n);
	print(n);
	return 0;
}

如何理解这个代码呢?我们不妨画图来理解一下:

 函数自己调用自己,但最后还要一步一步回归,这就是递归。

好了,这就是全部的内容,感谢大家的观看。

创作不易,可以给老弟一个小星星吗,感谢。

  • 13
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值