本文主要是介绍【C语言必经之路——第6节】函数内容总结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
一、什么是函数
二、C语言中的函数
1、分类
2、库函数
3、自定义函数
(1)函数的定义
(2)函数的参数
(3)函数的调用
(4)作用域
(5)声明与定义
三、多维数组作参数
四、函数的嵌套调用和链式访问
1、嵌套调用
2、链式访问
五、函数的递归与迭代
1、函数递归
2、函数迭代
3、举例
一、什么是函数
程序是由多个零件组合而成的,而函数就是这种“零件”的一个较小的单位。也可以叫子程序。
在计算机科学中,子程序是一个大型程序中的某部分代码, 由一个或多个语句块组成。它负责完成某项特定任务,而且相较于其他代码,具备相对的独立性。一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。
二、C语言中的函数
1、分类
1. 库函数
2. 自定义函数
2、库函数
在开发过程中常用的基础功能,它们不是业务性的代码,这些在开发的过程中每个程序员都可能用的到,为了支持可移植性和提高程序的效率,所以C语言的基础库中提供了一系列类似的库函数,方便程序员进行软件开发。使用库函数,必须包含 #include 对应的头文件。
C语言常用的库函数都有:
- IO函数
- 字符串操作函数
- 字符操作函数
- 内存操作函数
- 时间/日期函数
- 数学函数
- 其他库函数
如何学习库函数,在这里笔者推荐几个网站:
www.cplusplus.com
http://en.cppreference.com(英文版)
http://zh.cppreference.com(中文版)
3、自定义函数
自定义函数和库函数一样,有函数名,返回值类型和函数参数。但是不一样的是这些都是我们自己来设计,针对不同的功能用不同的函数来实现。
(1)函数的定义
ret_type fun_name(para1, * )
{
statement; //语句项
}ret_type 返回类型
fun_name 函数名
para1 函数参数
这里我们从一个例子细分析函数的组成
int max2 ( int a, int b ) ·接收int型的形参a、b。{ if ( a > b) ·求较大值。return a; else return b;} ·将求得的int型的值返回到调用源。
函数头( function header )
该部分表示函数的名称和格式。包含返回类型、函数名、形参声明三部分
返回类型( return type )
函数返回的值——返回值(return value)的类型。上述函数的情况下,返回的是两个int型数值中较大的一个,所以其类型是int。
函数名( function name )
函数的名称。从其他零件调用函数时,使用函数名。
形参声明( parameter type list ) 小括号括起来的部分,是用于接收辅助性提示的变量——形式参数的声明。像该函数这样接收多个形参的情况下,使用逗号将它们分隔开来。
函数max2中,a和b都被声明为了int型的形参。
函数体( function body )
函数体是复合语句。仅在某个函数中使用的变量,原则上应在该函数中声明和使用。但要注意不能声明和形参同名的变量,否则会发生变量名冲突的错误。
(2)函数的参数
实际参数(实参)
真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形
参。
形式参数(形参)
形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才分配内存单元,所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有
效。形式参数分配内存单元之后其实相当于实参的一份临时拷贝。
可以发现形参x,y与实参num1和num2的地址不同,而且第一次调用时主函数内的实参并没有交换,当第二次实参传地址,用形参指针接收时,数值才交换成功。可以看出来形参和实参并不是一个东西,只是将对应的值传给了形参在函数内部进行使用。还可以发现函数内部的“活动”不影响外函数的运行,只有进行传址调用,外函数的值才会与调用函数内部同步变化。
(3)函数的调用
传值调用
函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。
传址调用
传址调用是把函数外部创建变量的内存地址传递给函数参数的一种调用函数的方式。
这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部可以直接操
作函数外部的变量。
笔者的理解
形参是传来了地址,而且函数内容是对其所指的变量进行相关操作,这样就是传址调用,是可以对外部函数进行改变的。形参接收了地址而且函数是对地址操作,或者形参接收变量,函数对变量进行操作,这样就是传值调用,这样的函数对外部函数是不起作用的,操作区域只在函数内部。
三个例子
<1>传入地址,操作地址——不会影响实参
#include <stdio.h>
void fun(int *a,int *b)
{int *c;c=a;a=b;b=c;
}
int main()
{int x=3, y=5,*p=&x, *q=&y;fun(p, q);printf("%d %d\n", *p, *q);fun(&x, &y);printf("%d %d\n", *p, *q);
}
<2>传入变量值,操作变量——不会影响实参
#include <stdio.h>
void Swap1(int x, int y)
{int tmp = 0;tmp = x;x = y;y = tmp;
}int main()
{int num1 = 1;int num2 = 2;Swap1(num1, num2);printf("Swap1::num1 = %d num2 = %d\n", num1, num2);return 0;
}
<3>传入地址,操作变量——能够影响实参
#include<stdio.h>void Swap2(int* px, int* py)
{int tmp = 0;tmp = *px;*px = *py;*py = tmp;
}int main()
{int num1 = 1;int num2 = 2;Swap2(&num1, &num2);printf("Swap2::num1 = %d num2 = %d\n", num1, num2);return 0;
}
(4)作用域
赋给变量的标识符,它的名称都有一个通用的范围,称为作用域。
在程序块(复合语句)中声明的变量的名称,只在该程序块中通用,在其他区域都无效也就是说,变量的名称从变量声明的位置开始,到包含该声明的程序块最后的大括号为止,这一区间内通用。这样的作用域称为块作用域。
而在函数外声明的变量标识符,其名称从声明的位置开始,到该程序的结尾都是通用的。这样的作用域称为文件作用域。
(5)声明与定义
#include <stdio.h>
#define NUMBER 5 /*学生人数 */int tensu[NUMBER]; /*数组定义*/int top(void); /*函数top的函数原型声明*/
int main()
{extern int tensu[];int i;printf("请输入%d名学生的分数。\n",NUMBER);for (i = 0; i < NUMBER; i++){printf("%d: ", i + 1);scanf("%d", &tensu[i]);}printf("最高分 = %d\n", top( ));return 0;
}
int top(void)
{extern int tensu[]; /*数组的声明(可以省略)*/int i;int max = tensu[0];for (i = 1; i < NUMBER; i++)if (tensu[i] > max)max = tensu[i];return max;
}
<1> 编译器在读取数据时,也是按照从头到尾的顺序依次读取的。因为本程序中函数top的函数定义在main函数后,所以要想在main函数中调用top函数,编译器就需要知道函数top是什么。因此需要使用第七行的声明(函数top无需参数,并且会返回int型的值)。
像这样明确记述了函数的返回类型,以及形参的类型和个数等的声明称为函数原型声明。
需要注意的是该声明要以分号结尾。
函数原型声明只声明了函数的返回值和形参等相关信息,并没有定义函数的实体。
<2> 21行开始就是top函数的定义声明,包含函数体,是对函数的定义。
另外,如果函数top的需求(返回值的类型和形式参数等)发生了改变,那么函数定义和函数原型声明两部分都必须进行修改。
但是,在编写程序的时候,如果把函数top的函数定义放在main函数之前,就不用特意使用函数原型声明了。
三、多维数组作参数
接收多维数组的函数,可以省略相当于开头下标的n维的元素个数。但是,(n - 1)维之下的元素个数必须使用常量。
下面是接收一维数组~三维数组的参数的声明示例:
void funcl(int v[ ],int n); /*元素类型为int、元素个数随意(n)。*/
void func2(int v[ ][3], int n); /*元素类型为int[3]、元素个数随意(n)。*/
void func3(int v[ ][2][3],int n) ; /*元素类型为int [2] [3]、元素个数随意(n)。*/
所接收的数组的元素类型必须固定,但元素个数是自由的。
四、函数的嵌套调用和链式访问
函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的。
1、嵌套调用
函数可以嵌套调用,但是不能嵌套定义。下面代码就是主函数main调用three_line()函数,three_line()函数内调用new_line()函数。
#include <stdio.h>
void new_line()
{
printf("hello world\n");
}
void three_line()
{int i = 0;
for(i=0; i<3; i++){new_line();}
}
int main()
{
three_line();
return 0;
}
2、链式访问
链式访问就是把A函数的返回值作为B函数的参数。
void A()
{
/*函数体*/
}
void B()
{
A();
}
int main()
{
B();
}
#include <stdio.h>
int main()
{printf("%d\n", printf("%d\n", printf("%d\n", 43)));return 0;
}
五、函数的递归与迭代
1、函数递归
一个过程或函数在其定义或说明中有直接或间接调用自身的编程技巧称为递归。递归的主要思想在于:把大事化小。从字面意思可以理解为重复“递推”和“回归”的过程,当“递推”到达底部时就会开始“回归”。
递归的两个必要条件
存在限制条件,当满足这个限制条件的时候,递归便不再继续。
每次递归调用之后越来越接近这个限制条件。
2、函数迭代
从初始状态开始,每次迭代都更新状态,多次迭代直到到达结束状态。说白了迭代就是一个循环,从初始值进入,不断的计算变化成新的值,最后在末状态输出。
3、举例
//递归法求n的阶乘
int factorial1(int n)
{if (n <= 1)return 1;elsereturn n * factorial1(n - 1);
}//迭代法求n的阶乘
int factorial2(int n)
{int result = 1;while (n > 1){result *= n;n -= 1;}return result;
}int main()
{int n = 5;printf("%d\n", factorial1(n));printf("%d\n", factorial2(n));
}
今天的分享就到这啦😉
如果我的文章对您有帮助,
请 “点赞” “收藏” “关注”
一键三连哦!
想了解更多知识请前往故里♡927的博客
如果以上内容有什么问题,欢迎留言,大家一起学习,共同进步。
我们下期见😉~~
这篇关于【C语言必经之路——第6节】函数内容总结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!