4.18笔记(函数)

函数是什么?

    维基百科中对函数的定义:子程序。

    · 在计算机科学中,子程序是一个大型程序中的某部分代码,由一个或多个语句块组成。它负责完成某项特定任务,且具备相对的独立性。
    · 一般会有输入参数和返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。

c语言中函数的分类:

  1. 库函数
  2. 自定义函数

库函数:一般是指编译器提供的可在c源程序中调用的函数。

为什么会有库函数呢?
库函数的使用是为了提高效率。如我们频繁的使用(printf)。

库函数的学习可以参考此网站:http://www.cplusplus.com/

使用库函数必须包含#include对应的头文件。

自定义函数: 由程序员自己设计的函数。和库函数一样,也包括函数名,返回值类型和函数参数。

c语言里面函数可以没有返回值,默认返回值int型。若实在没有返回值,可以写void,函数里面也可以没有形参,为空,可以写上void

如这样一个函数:

Max(int m, int n)
{
    
    
	return m > n ? m : n;
}
int main()
{
    
    
	int x = 5;
	int y = 4;
	printf("%d\n", Max(y, x));
	return 0;
}

结果为:
在这里插入图片描述
默认为int型返回了,一般在自定义函数,建议加上返回类型。

函数的参数:

  1. 实际参数(实参):真实传给函数的参数,叫实参。实参可以是:常量,变量,表达式,函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传给形参。
  2. 形式参数(形参):形式参数是指函数名后括号内的变量,因为形式参数只有在函数被调用的过程才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。

形参也是有自己的地址的:

int Max(int m, int n)
{
    
    
	printf("m的地址为%p\n", &m);
	printf("n的地址为%p\n", &n);
	return m > n ? m : n;
}
int main()
{
    
    
	int x = 5;
	int y = 4;
	printf("x的地址为%p\n", &x);
	printf("y的地址为%p\n", &y);
	printf("%d\n", Max(y, x));
	return 0;
}

结果为:

形参实例化的过程一定会分配内存单元,就会有相应的地址。

函数的调用:

  1. 传值调用:函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
  2. 传址调用:传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。这种传参方式可以将函数和函数外边的变量建立起真正的联系,也就是函数内部可以操作函数外部的变量。

传址:如这样一个代码

int Swap(int *m, int *n)
{
    
    
	int t = *m;
	*m = *n;
	*n = t;
}
int main()
{
    
    
	int x = 5;
	int y = 4;
	Swap(&x, &y);
	return 0;
}

在这里插入图片描述
对形参进行了操作,影响了实参的值。

 
 
 
数组作为函数参数时,如果传入了一个数组,真正传参时,要发生数组的降维问题。
降维问题:降维成对应的指针。
为什么要进行降维呢?
提高效率,通过降低拷贝成本达到的。

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

  1. 嵌套调用:在调用一个函数当中,再继续调用函数。
  2. 链式访问:把一个函数的返回值作为另一个函数的参数。
int main()
{
    
    
	printf("%d ", printf("%d ", printf("%d ", 43)));
	return 0;
}

链式访问中,这样的结果是啥呢?

printf函数是有返回值的,返回值的为字符串的长度。

结果为:
在这里插入图片描述
函数的声明和定义:

  1. 函数声明:
    · 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体存不存在,无关紧要。
    · 函数的声明一般出现在函数的使用之前,要满足先定义再使用
    · 函数的声明一般放在头文件中。
  2. 函数的定义:函数的定义是指函数的具体实现,交代函数的功能实现。

函数递归:

什么是递归?
程序调用自身的编程技巧为递归。一个过程或者函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个 大型复杂的问题转化为一个与原问题相似的规模较小的问题来求解 ,递归中只需要少量的程序就可描述出解题过程所需要的多次重复计算,大大减小了程序的代码量。递归的主要的思考方式是:把大事化小。

递归的两个必要条件:

  1. 存在限制条件,当满足这个限制条件的时候,递归便不再继续。(递归出口)
  2. 每次递归越来越接近这个限制条件。

在c程序地址空间,函数和变量的分配地址是在栈区的。
栈区:先进后出,向下增长。
一个函数在栈上的分配空间又叫栈帧。
局部变量生命周期是随函数的(代码块)。

在某些递归调用中,有着大量的重复计算,函数调用栈帧,就会导致栈溢出,系统分配给程序的栈空间是有限的,但是如果出现了死循环,或者死递归,这样有可能导致一直开辟栈空间,最终产生栈空间耗尽的情况,这种现象就叫栈溢出。

如何解决呢?
· 将递归改为非递归
· 使用static对象替代nonstatic局部对象。可以减少每次递归调用和返回时产生和释放nonstatic对象的开销,而且static对象还可以保存到递归调用的中间状态,并且可为各个调用层所访问。

猜你喜欢

转载自blog.csdn.net/w903414/article/details/105583917
今日推荐