#函数全方面讲解# #万字巨作#❤️❤️❤️

13 篇文章 0 订阅
1 篇文章 0 订阅


1.函数的概念:

1)函数是将一行或多行的代码组织在一起,可以实现一定功能的程序。函数在C/C++语言中也叫做子程序,在JAVA等一些面向对象的语言中也叫方法。

2)所有的高级语言中都有函数或子程序这个概念,一个较大的程序一般应分为若干个程序块,每一个模块用来实现一个特定的功能。不能让main函数做几千几万行代码的事,要建立不同的函数来分工处理任务。

2.函数的作用:

a)如果把main函数比作公司的总经理,那么其他函数经理下面的部门经理或员工;

b)不能让main函数做几千几万行代码的事,要建立不同的函数来分工处理任务;

c)一般情况下,一个程序内的全部函数之间是以树状相互调用的。就如同一个公司的总经理安排工作要调用部门经理,部门经理再调用部门的员工等,层层调用到达树状结构的枝与叶;

d)函数的可以让代码更加容易维护,就如同文章中有各个不同段落;

e)函数)让代码重用性高,比如可以对同一个函数多次调用,不用多次编写相同的代码。

下面展示 相关代码片

 [例6-1]不调用函数:先求2的5次方,再求3的4次方,再求x的y次方
#include<stdio.h>

int main()

{

       int i = -1;

       int x = 2,y=5,n=1;

       while(++i<y)

              n*=x;

       printf("%d的%d次方是:%d\n",x,y,n);

       i=-1,x=3,y=4,n=1;

       while(++i<y)

              n*=x;

       printf("%d的%d次方是:%d\n",x,y,n);

       printf("请输入任意两个整数(以空格间隔):");

       scanf("%d%d",&x,&y);

       i=-1,n=1;

       while(++i<y)

              n*=x;

       printf("%d的%d次方是:%d\n",x,y,n);

       return 0;

}

a)先用口算或笔算方式算出代码中三个变量运算后的结果,观察与程序打印的结果是否一致;

b)请思考:本例中有三段循环代码,要是实现的功能是否相似?有什么办法可以减少重复的代码?

3.函数的定义

为了解决上一节的例程中,重复代码过多的问题,我们来学习函数的定义:

要定义一个函数,一般要先思考清楚这个函数要实现怎样的功能

经过对上一节中例程代码的思考之后,我们希望做这样一个函数:
只要将任意两个数值(m代表底数,n代表指数)带入这个函数之后,这个函数就能自动算出m的n次幂的结果。

在这里插入图片描述

图6-1 函数的定义

所谓定义一个函数,就是要设计和编写一个函数的意思。

一个完整的函数定义,包括函数头和函数体两部分。函数头就是函数大括号上方的第一行,函数体就是大括号内的一行或多行的代码。

一.参数列表的作用

1)根据程序设计者的需要,一个函数的参数列表中可能没有参数,也可能有1个、2个或者多个参数。

2)如图6-1所示,power函数中包含2个参数nNumb和nPow。

3)实际这2个参数和nRes一样,也是power函数内的两个int类型的临时变量。

4)所不同的是在程序进入函数之前,参数列表中的两个变量已经被建立好。并且在程序进入函数之前,主调函数必须要给这两个参数传入对应的数据,用于在power函数内参与计算。

5)参数传递的过程可以比喻为:

公司的经理如果要让财务部计算出A员工本月应发工资是多少,就必须先要给财务部提供A员工的按月工资是多少,还要提供这个员工本月出勤与请假的天数。这样财务才能算出这个员工的本月应发工资是多少,否则如果不给必要的数据财务部也是束手无策的。

如果经理又要财务部计算出B员工本月应发工资是多少,那他就必须要提供给财务部B员工的月工资额和本月出勤天数;如果经理要计算C、D、E…员工的应发工资,那他都必须要提供员工的数据给财务部门,财务部门才能为他计算出所需的结果。

下面展示 相关代码片

[例6-2]调用函数:先求2的5次方,再求3的4次方,再求x的y次方

#include<stdio.h>

int power(int nNumb, int nPow)

{

       int nRes = 1;

       while (nPow--)

              nRes *= nNumb;

       return nRes;

}

int main()

{//先求2的5次方,再求3的4次方,再求m的5次方。

       int x = 0,y=0;

       int n = power(2,5);

       printf("%d的%d次方是:%d\n",2,5,n);

       x = 3,y=4;

       n = power(x,y);

       printf("%d的%d次方是:%d\n",x,y,n);

       printf("请输入任意两个整数(以空格间隔):");

       scanf("%d%d",&x,&y);

       printf("%d的%d次方是:%d\n",x,y,power(x,y));

       return 0;

}

a)先用口算或笔算方式算出三次调用power函数后的打印结果,再观察是否与程序打印的结果一致;

b)在调试模式下按F10快捷键单步执行,在程序运行到了main函数中的power函数调用处,一定要按F11快捷键让程序进入到power函数内部;

c)在进入到power函数时,一定要先观察函数内的三个变量内的数值是多少?(包括:2个参数变量nNumb和nPow,以及1个函数内临时变量nRes) ;

d)在每次进入power函数之后,都继续按F10快捷键单步跟踪函数内的循环过程,认真观察程序在函数内执行每一步运算之后变量内的数据变化情况。

注意:一般C语言编译器大多都是F10快捷键代表逐过程(Step Over),F11快捷键代表逐语句(Step Into)。

VS系列编译器以及**早期VC++**各个版本编译器都是如此,如果是你用的是比较特殊的C语言编译器(例如:苹果电脑的xCode等),那你就只能自己多学习一下该编译器的相关调试快捷键了。

二.返回值的类型

按照返回值类型来划分,函数可以分为:

有返回值的函数 无返回值的函数 :两个种类。

1)、有返回值的函数的特点是:

a)函数的返回值类型是具体的类型(如:int、short和double等),而不是void类型;

b)有返回值类型的函数内,必须有return语句用于返回运算结果给主调函数;

c)有返回值类型的函数内,如果没有return语句的话代码编译时会出错。

d)有返回值类型的函数返回后,往往会把返回值传递给一个变量保存起来。

2)、无返回值的函数的特点是:

a)函数的返回值类型是void类型,而不是其他任何具体的类型;

b)无返回值类型的函数内,即使没有return语句编译也不会出错;

c)无返回值类型的函数内也可以有return语句,但return之后也不附带任何变量或表达式;

d)主调函数在调用无返回值类型的函数时,不允许使用变量或表达式来接收被调函数的返回值。

下面展示 相关代码片

 [例6-3]测试无返回值函数

#include<stdio.h>

int power(int nNumb, int nPow)

{//有返回值函数

       int nRes = 1;

       while (nPow--)

              nRes *= nNumb;

       return nRes;

}

void display(int nNumb,int nPow)

{//无返回值函数

       int n = power(nNumb,nPow);

       printf("%d的%d次方是:%d\n",nNumb,nPow,n);

}

int main()

{//先求2的5次方,再求3的4次方,再求m的5次方。

       int x = 0,y=0;

       display(2,5);

       display(3,4);

       printf("请输入任意两个整数(以空格间隔):");

       scanf("%d%d",&x,&y);

       display(x,y);

       return 0;

}

a)先用口算或笔算方式算出三次调用display函数后的打印结果,再观察是否与程序打印的结果一致;

b)在调试模式下按F10快捷键单步执行,在程序运行到了main函数中的display函数调用处,一定要按F11快捷键让程序进入到display函数内部;

c)在每次进入display函数运行到power函数调用处时,一定要再按F11键让程序进入到power函数内部;

d)在进入到power函数时,一定要先观察函数内的三个变量内的数值是多少?(包括:2个参数变量nNumb和nPow,以及1个函数内临时变量nRes) ;

e)在每次循环结束之后观察nRes变量内的数值是多少,离开power函数回到display之后,观察n变量内的数值多少。

三.主调函数与被调函数

首先:
1)大家必须明确一个问题:编写一个函数的目的,就是要用来被其他程序或函数调用的。

2)如果一个函数编写好了,却未被任何程序或其他函数调用的话,那么这个函数就是一段废弃的代码。正如一个人活着就必须对社会有可利用的价值,否则这个人就是一个废人。

3)主调函数被调函数是相对而言的。
在[例6-3]中,相对于power函数来看,
display函数是主调函数,
power函数是被调函数。
4)相对于main函数,display函数则是被调函数,而main函数是主调函数。

4)main函数在一个软件工程中,是所有其他函数的最终调用者

1*可以把main函数比作是一棵大树的根,
其他函数则是大树的枝和叶。

2*也可以把main函数比作是公司的总经理,
其他函数则是部门经理或部门员工。

3*程序执行的过程是从总经理开始,
总经理调用部门经理,
部门经理再调用其他员工等等。

4*表面上看,main函数只有调用其他函数,
而没有被其他任何函数调用。
实际上,在每个软件工程中,
main函数都是被操作系统调用的。
因此,我们把main函数称之为主函数入口函数

四.return关键字的开发技巧

1)在有返回值函数中,return关键字主要是负责向主调函数送回本函数的计算结果。

2)在无返回值函数中,return关键字主要是让函数在运行的中途结束,不负责向主调函数送回计算结果。

3)在实际开发中,当return语句与分支和循环语句结合时,有很多种开发的技巧。

4)本文所介绍的各种return语句的开发技巧,小白们应该认真学习和牢记,以便在以后的开发中使用最好的编程技巧。

5)当然,即使现在不牢记这些代码的编写技巧,当你在参与实际程序开发工作时间久了之后自然也会领略到的。
下面展示 相关代码片

[例6-4]判断一个数字是否为素数(是返回1,否返回0)

#include <stdio.h>

#include <math.h>

int IsPrime(int n)

{

       int i = 2;

       int m = (int)sqrt(n)+1;

       while (i < m )

       {

              if (n%i == 0)

                     break;

              ++i;

       }

       if(i >= m)

              return 1;

       else

              return 0;

}

int main()

{

       int n = 0;

       printf("请输入任意一个数字:");

       scanf("%d",&n);

       if(IsPrime(n))

              printf("%d是素数\n",n);

       else

              printf("%d不是素数\n",n);

       return 0;

}

a)任意输入一个素数或者非素数的数字,之后观察程序打印的结果是否正确;

b)在调试模式下按F10快捷键单步执行,在程序运行到了main函数中的IsPrime函数调用处,一定要按F11快捷键让程序进入到IsPrime函数内部;

c)在进入到IsPrime函数时,观察函数内包括形式参数在内的三个变量内的数值是多少?

d)在循环结束之后观察i与m变量内的数值是多少,预估回到main函数之后将如何继续运行?

请思考:

IsPrime函数中的多个return语句是否可以简化为更简单的代码?

结论:

我们对[例子6-4]中的IsPrime函数进行改造,可以得到很多种更简单的代码格式。

下面展示 相关代码片

[改造代码-A]带return语句的双分支语句,else关键字可以省略

int IsPrime(int n)

{

       int i = 2;

       int m = (int)sqrt(n)+1;

       while (i < m )

       {

              if (n%i == 0)

                     break;

              ++i;

       }

       if(i >= m)

              return 1;

       return 0;

}


按照以上代码修改[例6-4]的代码之后,编译、运行并查看程序运行结果是否与原来相同。

请思考:

是否可以将以上3行或4行的return语句合并为一行代码?

下面展示 相关代码片

[改造代码-B]使用三目的条件运算符将return语句合并为一行代码

int IsPrime(int n)

{

       int i = 2;

       int m = (int)sqrt(n)+1;

       while (i < m )

       {

              if (n%i == 0)

                     break;

              ++i;

       }

       return i >= m?1:0;

}

使用以上的三目条件运算符再次修改代码,编译、运行并查看程序运行结果是否与原来相同。

请思考:

是否可以将以上的条件运算符也去掉,用更简单的一行代码实现与原来相同的程序效果?

下面展示 相关代码片

[改造代码-C]判断型函数直接返回比较结果

int IsPrime(int n)

{

       int i = 2;

       int m = (int)sqrt(n)+1;

       while (i < m )

       {

              if (n%i == 0)

                     break;

              ++i;

       }

       return i >= m;

}

再次修改代码,直接将比较结果返回,编译、运行并查看程序运行结果是否与原来相同。

注意:比较运算符的运算结果本来就是1和0(真是1,假是0)。
因此,今后再遇到判断型函数时,可以考虑直接将比较运算符的结果返回。

请思考:

是否可以将以上的比较运算符也去掉,用更简单的代码实现与原来相同的程序效果?

下面展示 相关代码片

[改造代码-D]用return替代break语句,让程序更简单更直观

int IsPrime(int n)

{

       int i = 2;

       int m = (int)sqrt(n)+1;

       while (i < m )

       {

              if (n%i == 0)

                     return 0;

              ++i;

       }

       return 1;

}

再次修改代码,用return替代break语句,编译、运行并查看程序运行结果是否与原来相同。

注意
1)在中途结束的循环程序中,使用return语句代替break,往往可以让程序更简单更直观。
2)break关键字让循环结束,继续执行循环之后的代码。return是比break更强的中断关键字,不但循环会立即终止,而且当即离开当前函数。

五.形式参数与实际参数

1.形式参数(parameter) (简称形参),就是在定义函数时在函数头的参数列表内的参数。

2.在C语言中,形参可以认为是被调函数内,用来接收主调函数传递数据的临时变量。

1.实际参数(argument)(简称实参),在主调函数中调用一个函数时,函数名后面括弧中的参数,实参可以是常量、变量、表达式或者有返回值的函数等。
2.实参的个数必须与被调函数的形参个数一致,并且每个参数的类型也尽量一致。简单地说,实参与形参在个数和类型上,必须要一一对应的,否则编译的时候就可能会出现错误。
下面展示 相关代码片

[例6-5]多种不同形式的函数编写(参数不同、返回值也不同)

#include <stdio.h>

#include <math.h>

int IsPrime(int n)

{

       int i = 2;

       int m = (int)sqrt(n)+1;

       while (i < m )

       {

              if (n%i == 0)

                     return 0; //不是素数

              ++i;

       }

       return 1; //素数

}

void PrintPrime(int x, int y)

{

       int i = x;

       int sum = 0;

       printf("%d到%d之间的素数包括:\n", x, y);

       while (i < y)

       {

              if (IsPrime(i))             //等价于if (IsPrime(i) != 0)

              {

                     printf("%d ", i);

                     ++sum;

              }

              ++i;

       }

       printf("\n总共有%d个素数\n\n", sum);

}

int main()

{

       PrintPrime(10, 80);

       PrintPrime(100, 200);

       PrintPrime(280, 520);

       return 0;

}

a)在main函数中,对PrintPrime函数代入0个、1个或多个参数再编译,观察是否出错,例如:PrintPrime();

b)在main函数中,对PrintPrime函数代入浮点数再编译,观察是否出错。例如:PrintPrime(10.88,80.01);

c)在PrintPrime函数中,对IsPrime函数的调用处,代入2个或2个以上的参数再编译,观察是否出错;

d)在main函数中,将PrintPrime函数的返回值赋值给一个变量n再编译,观察是否出错。

结论:

a)被调函数的形参为主调函数提供了调用模板,主调函数必须依照形参的个数和类型调用被调函数;

b)如果实参的个数与形参不同编译会出错,如果实参的类型与形参不同编译也可能会出错。例如:实参是不同类型的数组或指针(下一章的内容);

c)当形参是整数或浮点数,如果实参的类型小于形参时编译也不出错。如果实参的类型大于形参时(例如:形参是整数实参是浮点数),编译会出现警告(warring);

d)调用返回值是void类型的函数时,如果将返回值打印或赋值给其他变量,编译时会出错。

六.嵌套循环与函数:

1)嵌套循环就是指在一个循环体语句中又包含另一个循环语句。

2)当一次外层循环循环执行一次,内层循环要执行0到多次。

3)每次内层结束后,外层循环再继续执行下一次循环。

4)使用嵌套循环时,内层循环和外层循环的循环控制变量不能相同,否则容易造成内外循环层的混乱。在设计嵌套循环时,尽量优先编写完内层循环之后再编写外层循环,这样思路就会比较清晰。

下面展示 相关代码片

[例6-6]将上一节的两个函数合并为一个函数(嵌套循环)

#include <stdio.h>

#include <math.h>

void PrintPrime(int x, int y)

{

       int n = x;

       int sum = 0;

       printf("%d到%d之间的素数包括:\n", x, y);

       while(n < y)

       {

              int i = 2;

              int m = (int)sqrt(n)+1;

              while (i < m)

              {

                     if (n%i == 0)

                            break;

                     ++i;

              }

              if(i==m)

              {

                     printf("%d ", n);

                     ++sum;

              }

              ++n;

       }

       printf("\n总共有%d个素数\n\n", sum);

}

int main()

{

       int x,y;

       while(1)

       {

              printf("请输入两个正整数[-1代表退出]:");

              scanf("%d",&x);

              if(x==-1)

                     break;

              scanf("%d",&y);

              PrintPrime(x,y);

       }

       return 0;

}

结论:

a)嵌套循环的运行速度比独立函数快,因为节省了进入函数和离开函数(入栈与出栈)耗费的时间;

b)独立函数的代码清晰直观,便于程序员对代码阅读和维护,嵌套循环代码不容易阅读和理解;

                               BY 白巾-子木
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值