【C语言详解】函数

2021-11-10-

作者:Nico
时间: 2021-11-10-
网站地址:sxfinn.com

摘要

数学中我们时常见到函数,C语言也同样,函数在在C语言中应用十分广泛,可以说不会使用函数就很难将编程的学习进行下去了,好好理解并掌握函数会让我们在往后地学习中更加轻松。本文将介绍函数的定义、函数的分类、函数的结构、函数的使用以及函数特定情况下巧妙地使用方法。

总结

函数是一个大型程序中的完成特定工作的独立程序模块, 由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性,包括库函数和自定义函数两种。

目录

  • 2021-11-10-
      • 摘要
      • 总结
    • 目录
    • 函数
      • 函数是什么
      • 函数的分类
      • 函数的参数
      • 函数的调用
      • 函数的嵌套和链式访问
      • 函数的声明和定义
      • 函数递归


函数

函数是什么

C语言的基本结构单位是函数[维基百科]。系统首先调用 main函数(主函数),通过函数的嵌套调用,再调用其他函数。函数可以是系统自带的函数,也可以是用户定义的函数。C语言中,不允许函数嵌套定义。

数学中我们常见到函数的概念。但是你了解C语言中的函数吗? 维基百科中对函数的定义:子程序 在计算机科学中,子程序(英语:Subroutine, procedure, function, routine, method, subprogram, callable unit),是一个大型程序中的某部分代码, 由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。 一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。这些代码通常被集成为软件库。

函数的分类

  • 库函数

  • 自定义函数

库函数

库函数是C语言已经提供给我们可以直接去使用的函数,例如:printf()函数、scanf()函数,编程时直接调用即可。

为什么会存在库函数呢?

在编程的过程中我们会频繁的做一些字符串的拷贝工作(strcpy)。

在编程是我们也计算,总是会计算n的k次方这样的运算(pow)。

像上面我们描述的基础功能,它们不是业务性的代码。我们在开发的过程中每个程序员都可能用的到,为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。

下面来看一些常用的数学函数:

  1. 平方根函数sqrt(x):如sqrt(4.0)的值为2.0。
  2. 绝对值函数fabs(x):如fabs(-3.89)的值为3.89。
  3. 幂函数pow(x):如pow(2,3)的值为8。
  4. 指数函数exp(x):计算ex 如exp(2.3)的值为9.974182。
  5. 对数函数log(x):计算以e为底的对数。如log(e2)的值为2。

简单的总结,常用的库函数有:

  • IO函数
  • 字符串操作函数
  • 内存操作函数
  • 时间函数
  • 数学函数
  • 其他库函数

如何学会使用库函数?

查阅文献,利用函数文档。

c++库函数

大家可以直接访问,并且这个网站是有中文版本的。

char * strcpy ( char * destination, const char * source );//函数原型

例如上面:就可以直接看到有函数名,参数以及返回类型。

注意:但是库函数必须知道的一个秘密就是:使用库函数,必须包含 #include 对应的头文件。

自定义函数

库函数可不是万能的,有一些功能需要我们自己去实现,与库函数一样,自定义函数也有函数名,参数以及返回类型,不同的是这些都需要我们自己来设计,这给了程序员们很大的发挥空间。

//函数的组成
return_type fun_name(para1,para2,...)
{
     
    statement;//语句
}
//return_type:函数返回类型
//fun_name:函数名
//para:参数

我们来举个例子:

写一个可以交换两个变量值的函数

//传递值函数
#include
void swap(int x, int y)
{
     
	int tmp = x;
	x = y;
	y = tmp;
}
//传递地址函数
void swap(int* px, int* py)
{
     
	int tmp = *px;//创建临时变量
	*px = *py;
	*py = tmp;
}
int main()
{
     
	int x = 3;//初始化值
	int y = 6;
	printf("交换前:x=%d y=%d\n", x, y);
	swap1(x, y);//swap1交换
	printf("swap1:x=%d y=%d\n", x, y);
	swap2(&x, &y);//swap2交换
	printf("swap2:x=%d y=%d\n", x, y);
	return 0;
}

输出结果:

交换前:x=3 y=6

swap1:x=3 y=6

swap2:x=6 y=3

可以看到swap1并没有产生我们想要的效果,为什么传值后主函数的变量x,y并没有变化呢?

下面来介绍函数的调用过程,后面会详细说明。

函数的参数

实际参数

实参是在调用函数真实传递给函数的参数,实参的类型可以是:

  • 表达式
  • 常量
  • 变量
  • 函数

无论是何种类型的参数,在调用函数传参时一定要有确定的值,这里注意是传递值,不是传递变量。

形式参数

形式参数是函数名后括号内的变量,简称形参。

形参在没有调用函数时,没有自己的内存空间,只有在函数被调用时形参才在内存中被分配空间并接受传递到函数的值。

因此叫做形式参数,在函数结束调用后自动销毁,形式参数只在函数中有效。

注意:函数的形参和实参分别拥有不同的内存空间。

函数的调用

传值调用

函数的形参时实参的一份临时拷贝,对形参的修改不会影响实参。

传址调用

当我们需要对我们的实参进行修改时,采用传址调用,顾名思义就是将我们想要操作的变量的地址传递给形参。

这种参数传递方式可以让函数内外的变量建立练习,而起到这样作用的,正是地址,可以在函数内部修改函数外部变量的值。

void add(int num)
{
     
	num++;
}
int main()
{
     
	int num = 0;
	add(num);
	return 0;
}

main函数中的num并不会因为add函数中num的改变而改变。

函数的嵌套和链式访问

函数和函数之间是可以相互调用的,可以在一个函数中借用另一个函数的功能。

嵌套调用

嵌套调用就是在一个函数中调用另一个函数,可以根据实际应用对函数进行组合,从而达到我们的目的。

代码:

void print()
{
     
	printf("hehe");
	print();
}
int main()
{
     
	print();
	return 0;
}

输出结果是满屏幕的hehe

可以嵌套使用,定义不可嵌套

链式访问

链式访问就是将一个函数的返回值作为另一个函数的参数

int main()
{
     
	int num = 345;
	printf("%d ", printf("%d ",num));
	return 0;
}

输出:345 3

这就是函数的链式访问。

printf函数的返回值是打印在屏幕上字符的个数。

函数的声明和定义

函数声明

函数声明就是告诉编译器我这里有一个函数,声明包括函数的返回类型,函数名以及参数类型,并不用写上参数的名称,当然写上也没问题。

函数一定要先声明才能使用。

函数的声明一般放在头文件里。

声明:void print();

函数定义

函数定义就是函数的具体实现过程。

代码:

void print()
{
     
	printf("hehe");
	print();
}

函数递归

什么是递归

函数在自己调用自己的情况就是递归。

递归作为一种技巧性很强的算法在程序设计中应用极为广泛。

通常一些大型复杂问题往往可以通过递归简化,使用递归算法,可以让原问题层层转化为一个与原问题相似的一个个小问题来解决。

可以利用递归性质,是用少量代码让计算机完成大量运算。

递归的必要条件

  1. 存在限制条件来结束递归。
  2. 每次递归不断接近这个限制条件。

示例代码:

求n的k次方

//实现n的k次方
float power_function(float n, int k)
{
     
	if (k == 0)
		return 1;
	else if (k > 0)
		return n * power_function(n, k - 1);
	else
		return (1 / n) * power_function(n, k + 1);
}

递归的主要思想就是将繁化简。

递归的缺点:

重复运算多次。

int cnt = 0;//全局变量,记录递归次数
int fib(n)
{
     
	cnt++;
	if (n <= 2)
		return 1;
	return fib(n - 1) + fib(n - 2);
}
int main()
{
     
	int n = 20;
	int ret = fib(n);
	printf("ret=%d cnt=%d", ret,cnt);
	return 0;
}

输出:ret=6765 cnt=13529

可以看到简简单单几行代码,运算次数却如此之多。

注意:

  1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。
  2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
  3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。

你可能感兴趣的:(C语言初阶学习,c语言,开发语言,后端)