10讲学会C语言之第五讲:函数


前言

大家好,我是卷卷。本节课是第五讲,函数。本节课主要有这五个部分:计算圆柱体积,数字金字塔,结构化程序设计,复数运算,作业。(讨论q群号744931080,教程资源在群内)

一、计算圆柱体积

例1:输入圆柱的高和半径,求圆柱体积,要求定义和调用函数volume(r,h)计算圆柱体的体积,首先高和半径都是小数,所以都用浮点型变量。设double型变量h表示高,r表示半径,则圆柱体积也是double型。所以函数的返回值是double。参数都是double型。在函数体内计算体积,然后返回即可。本题的重点是形式参数,返回值,函数的调用。代码:

#include<stdio.h>
double volume(double r,double h);
int main(){
	double h,r;
	printf("输入半径和高:");
	scanf("%lf %lf",&r,&h);
	printf("圆柱体积为:%.3f\n",volume(r,h));
	return 0;
}
double volume(double a,double b){
	return 3.1415926*a*a*b;
}

首先第一个函数,传入r和h。然后返回计算结果。然后在主函数中调用即可。我们试一下:在这里插入图片描述
这就是本题的运行结果了。对于一个函数而言最重要的就是传入的参数,当然可以没有参数。如果有参数,就要注意一个概念:形式参数。我们所传入的变量都叫做实际参数。比如说这里的r和h都是实际参数。而传入函数以后的参数叫做形式参数,他们不是原来的实际参数,而是实际参数的一个副本。也就是在传入参数的时候,函数会在内存中开辟几块空间,然后复制实际参数的值。所以函数中的变量a和b不是原来的变量,只是原来变量的副本而已。对于形式参数,可以取任何变量名。变量名可以和实际参数一样,也可以不一样。比如说把a,b改为r,h是可以的,但是改了以后,返回值就要做改动了。我们来试一下:在这里插入图片描述
结果是一样的。对于返回值,像上图这种是一个简便的写法,即直接写表达式。表达式计算完后,它的结果就作为一个变量被返回了。我想大家现在已经理解了函数的参数,这就是本题的讲解了。

例2:定义一个判定奇偶数的函数even,当n为偶数时返回1否则返回0。显然参数是整数,返回的也是整数。只需判断n模2是否为0,返回结果即可。这里题目只要求定义函数,所以在代码里无需调用。当然大家也可以课后手动去调用一下。代码:

int even(int n){
	if(n%2==0)
		return 1;
	return 0;
}

首先传入一个整数,然后判定。如果它是偶数就返回1,如果不是就返回0。因为在返回语句执行后会立即退出函数。所以这里可以不用else语句。这里补充一个比较简单的做法:return n%2 == 0?1:0;
这个表达式是三目运算符表达式。它有三个参数,所以它的符号叫作三目运算符。n%2==0是第一个参数,1是第二个参数,最后的0是第三个参数。如果第一个参数的表达式为真,就执行第二个参数,如果为假,就执行第三个参数。这里还可以进一步简写:return n%2?1:0;
效果和前面相反。原理是真用1表示,假用非零数表示。所以如果结果非零(奇数),就返回1,如果为0(偶数)就返回0。这里其实还可以进一步简写:return n%2;
这一步简写其实不是通用的,因为前面的那些表达式都是返回1或者0。而这里只是恰巧返回1或者0。因为n%2的结果只有两种,1或者0。如果你写上n模3,那它的结果就不止是1和0了。所以这里只是凑巧。好了,这就是本题的讲解了。

例3:求100以内的全部素数,每行输出10个。素数就是只能被1和自身整除的正整数。1不是素数,2是素数。要求定义和调用函数prime(m)判断m是否为素数,当m为素数时返回1,否则返回0。判断素数的算法比较简单,之前讲过,所以不做赘述函数。参数类型显然是int,返回类型也是int。代码:

#include<stdio.h>
#include<math.h>
int prime(int m);
int main(){
	int count=0,m;
	for(m=2;m<=100;m++)
	if(prime(m)!=0){
		printf("%6d",m);
		count++;
		if(count%10==0)
			putchar('\n');
	}
	putchar('\n');
	return 0;
}
int prime(int m){
	int i,n;
	if(m==1)return 0;
	n=sqrt(m);
	for(i=2;i<=n;i++)
	if(m%i==0)
		return 0;
	return 1;
}

这里还是做了一个优化的,m的上限是根号m,这里的根号函数要用math头文件。首先要判定m是否等于1,因为如果m等于1,它就是素数,直接返回0即可。然后再判定1以外的素数,这个算法我想大家已经比较熟悉了,不做赘述。在主函数内,外层循环这里没有花括号,如果没有花括号,它的执行范围就是一句。然后用调用函数来判定即可。注意这里是每隔10个换行,所以要模10。我们来看一下结果:在这里插入图片描述
这就是1到100以内的素数了。

二、数字金字塔

例4:输出5行的数字金字塔。算法和上一节课的练习4差不多,只不过之前是输出菱形,这里输出数字。本题也仅输出前(n+1)/2行,所以只要一个循环。函数参数显然是int,函数内容显然只需输出,无需返回,所以函数的返回类型是void。本题的重点是空返回类型。我们来看一下代码:

#include<stdio.h>
void pyramid(int n);
int main(){
	pyramid(5);
	return 0;
}
void pyramid(int n){
	int i,j;
	int space=n-1,num=1;
	for(i=1;i<=n;i++){
		for(j=1;j<=space;j++)
			printf(" ");
		for(j=1;j<=num;j++)
			printf("%d ",i);
		putchar('\n');
		space--;num++;
	}
}

首先传入一个整数,然后定义空格的数量和数字的数量,给它们初始化。然后循环n次,每轮循环输出一定数量的空格和数字。每轮循环结束前空格数减1,数字数加1。我们来运行一下:在这里插入图片描述
这就是五行的数字金字塔了。和上讲的练习也是差不多的,只不过一个是输出星号,这里是输出数字。这个空返回类型的作用是什么都不做,在执行到函数体内的最后一句后直接返回,也就是直接退出函数。

三、结构化程序设计

1.自顶向下分析问题的方法:这个方法就是把大的复杂的问题分解成小问题后再解决。首先进行上层整体的分析按,组织或功能将问题分解为子问题。如果子问题仍然十分复杂,再做进一步分解,直到处理对象相对简单容易解决为止。当所有的子问题都得到了解决,整个问题也就解决了。在这个过程中,每一次分解都是对上一层问题进行的细化和逐步求精。最终形成一种类似树形的层次结构,来描述分析的结果。例如开发一个学生成绩统计程序,输入一批学生的5门课的成绩,要求输出每个学生的平均分和每门课程的平均分,找出平均分最高的学生。按照上述分析问题的方法,可按功能将其分解为四个子问题:成绩输入,数据计算,数据查找(查找最高分)和输出成绩。其中数据计算又分解为计算学生平均分,和计算课程平均分2个子问题。其层次结构如图5.1所示:
            学生成绩统计程序模块图
在这里插入图片描述
这就是学生成绩统计程序的结构了。

2.模块化设计。设计好层次结构图后,就要进行模块化设计。模块化设计要遵循模块独立性的原则,即模块之间的联系应尽量简单。如下:
(1)一个模块只完成一个指定的功能。
(2)模块之间只通过参数进行调用。
(3)一个模块只有一个入口和一个出口。
(4)模块内慎用全局变量。
模块化设计使程序结构清晰,易于设计和理解。当程序出错时,只需改动相关的模块及其链接。在C语言中,模块一般通过函数来实现,一个模块对应一个函数。在设计某一个具体的模块时,模块中包含的语句一般不要超过50行,这既便于设计也便于阅读。如果该模块功能太复杂,可进一步分解到低一层的模块函数,以体现结构化的程序设计思想。根据图5.1,模块化设计如下:
(1)设计7个函数,每个函数完成一项功能,代表一个模块。包括:主函数main(),成绩输入input_stu(),数据计算calc_data(),计算学生平均分avr_stu(),计算课程平均分avr_cor(),数据查找(查找最高分)highest()和输出成绩output_stu()。
(2)模块间的调用关系为:主函数main()依次调用函数input_stu(),calc_data(),highest()和output_stu(),函数calc_data()中分别调用函数avr_stu()和avr_cor(),完成数据计算的功能。

3.结构化编程的主要原则。在设计模块时,程序员还应遵循以下原则,以便于维护和他人阅读:
(1)经过模块化设计后,每一个模块都可以独立编码。对于复杂的问题,可通过顺序选择和循环这三种结构的组合和嵌套实现,以清晰表达程序的逻辑结构。
(2)对变量,函数常量等命名时,要见名知义。如求和用sum,求阶乘函数用fact等。
(3)在程序中增加必要的注释,增加程序的可读性。序言性注释一般放在模块的最前面,给出模块的整体说明。状态性注释一般紧跟在引起状态变化语句的后面,予以必要说明。
(4)要有良好的程序视觉组织,利用缩进格式,一行写一条语句,呈阶梯形,使程序逻辑结构层次分明。
(5)程序要清晰易懂,语气构造要简单直接。在不影响功能与性能时,做到结构清晰第一,效率第二。
(6)程序要有良好的交互性,输入有提示,输出有说明,并尽量采用统一整齐的格式。

四、复数运算

例5,分别输入2个复数的实部与虚部,用函数实现计算2个复数之和与之积。若2个复数分别为c1=x1+y1i;c2=x2+y2i。则c1+c2=(x1+x2)+(y1+y2)i;
c1
c2=(x1x2-y1y2)+(x1y2+x2y1)*i。本题虽然是计算问题,但与前面的例子有本质区别。因为它的运算结果有两个数值,即复数的实部与虚部。那么函数就无法通过return返回,解决办法之一是采用全局变量real,imag作为结果,每次调用函数都去改变real和imag。显然函数的返回类型是void。本题的重点是局部变量与全局变量,我们来看一下代码:

#include<stdio.h>
double real,imag;
void product(double r1,double i1,double r2,double i2);
void add(double r1,double i1,double r2,double i2);
int main(){
	double r1,i1,r2,i2;
	printf("输入第1个复数:");
	scanf("%lf %lf",&r1,&i1);
	printf("输入第2个复数:");
	scanf("%lf %lf",&r2,&i2);
	add(r1,i1,r2,i2);
	printf("两复数之和为:%.2f+%.2fi",real,imag);
	product(r1,i1,r2,i2);
	printf("两复数之积为:%.2f+%.2fi",real,imag);
	return 0;
}
void add(double r1,double i1,double r2,double i2){
	real=r1+r2;
	imag=i1+i2;
}
void product(double r1,double i1,double r2,double i2){
	real=r1*r2-i1*i2;
	imag=r1*i2+r2*i1;
}

首先要定义两个全局变量,因为两个函数都要用到这些变量。而且一个函数只能有一个返回值,现在要返回两个值,我们只能通过修改全局变量来实现。目前来说这是一个比较方便的做法,当然还有别的方法,比如说结构体,可以直接返回一个结构体,然后读取结构体内的成员,结构体将会在第九讲讲解。这里大家只是知道一下概念,或者不知道也没关系。累加函数就是对实部和虚部进行相加,乘积函数就是对实部和虚部进行一些乘积运算,这些只要按照题目的定义写上即可。我们来运行一下:在这里插入图片描述
这就是本题的讲解了。

例6:用函数实现财务现金记账。先输入操作类型(1收入,2支出,0结束),再输入操作金额,计算现金剩余额,经多次操作直到输入操作类型为0时结束。要求定义并调用函数,其中现金收入与现金支出分别用不同函数实现。设变量cash保存现金余额。由于它被主函数、现金收入与现金支出函数共享。所以cash适合定义为全局变量。支出函数内cash-=金额,收入函数为cash+=金额。两个函数的返回类型显然都是void。我们来看一下代码:

#include<stdio.h>
double cash;
void income(double num);
void expend(double num);
int main(){
	int choice;
	double value;
	cash=0;
	printf("输入选项:0.结束 1.收入 2.支出:");
	scanf("%d",&choice);
	while(choice){
		if(choice==1||choice==2){
			printf("输入金额:");
			scanf("%lf",&value);
			choice==1?income(value):expend(value);
			printf("现有金额:%.2f\n",cash);
		}
		printf("输入选项:0.结束 1.收入 2.支出:");
		scanf("%d",&choice);
	}
	return 0;
}
void income(double num){
	cash+=num;
}
void expend(double num){
	cash-=num;
}

首先定义全局变量cash,然后再定义两个函数。代码非常简单,收入就是累加,支出就是累减。然后在main函数调用它即可。首先输出菜单,得到选项以后,再用一个循环。因为0表示结束,直接用while循环比较简洁。当输入0的时候存款就结束了,如果输入的是1或者2,则让用户输入金额,输入金额后再调用函数。这里用了三目运算符,如果选择为1,则调用收入函数,如果选择为0,则调用支出函数。然后输出现金即可,判定完以后,再输入菜单。然后让用户再次输入选项,如果选项为0,循环就结束了。我们试一下:在这里插入图片描述
这就是例题的讲解了。

五、作业

接下来是作业环节,我相信通过前几讲的练习,大家已经掌握了一定的算法能力。所以从本节课开始,作业的讲解就不会那么详细了。作业是6道例题加上4道练习题,总共10道题。练习1:在这里插入图片描述
这个题目类型和之前有所不同,这个是一个函数题,它有个裁判测试程序样例。这些代码是判定程序的代码,你要写的代码,就是写在这个花括号以后,也就是说你只需写一个函数即可。当然,做算法题是逃不过测试的,所以我建议直接把这段代码复制下来,然后写入你的代码。这些写在你的编程工具中,测试成功后,直接把你定义的函数写到输入框即可。当然没买教材的同学,直接在编程工具里测试即可。练习2:在这里插入图片描述
第二题是使用函数求素数和,求素数和我相信大家都不陌生 ,之前也已经练习过好几次。练习3:在这里插入图片描述
第三题是用函数统计指定数字的个数,我们直接看输入输出样例。这里显然是要用到模10,然后再除以10的算法,这题也很简单。练习4:在这里插入图片描述
最后一题会稍微要拐个弯,但是其实也不难。这里要注意,输入n位正整数 计算n次幂 ,也就是输入3位数,要计算3次幂。输入4位数,要计算4次幂。输入5位数,要计算5次幂。最多到5次,因为它这里给出了数字的范围。好了,这就是今天的全部内容了,我们下讲见!

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术卷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值