第五章 函数
5.1
5.1.2 函数的定义
函数是一个完成特定工作的独立程序模块,包括库函数和自定义函数两种。
自定义函数:
函数类型 函数名(形式参数表){
函数实现过程
}
1.函数首部
函数首部由函数类型、函数名和形式参数表组成。函数类型指函数返回结果的类型,一般与return语句中表达的类型一致。
参数表中给出函数计算所用到的相关已知条件,以类似变量定义的形式给出,例如:
double cylinder(double r, double h)
表示函数类型是double,也是函数的结果类型;函数名为cylinder,两个形参r和h的类型都是double,在cylinder()函数被调用时,两个形参的值将由主调函数给出。
注:形参不能写成 double r,h !!!
2.函数体
由一对大括号和里面的若干条语句组成,用以计算,或完成特定的工作,并使用return语句返回运算结果
5.1.3 函数的调用
定义一个函数后,就可以在程序中调用这个函数,在C语言中,调用标准函数库时,只需要在程序最前面用#include 命令包含相应的头文件,调用自定义函数时,程序中要有与调用函数对应的函数定义。
...
3.参数传递
定义函数中double cylinder(double r, double h)指明两个形参r和h,而main()函数中volume=double cylinder(redius, height);中的redius和height是实参,实参redius和height的值将会传递给形参r和h。
实参要和形参一一对应,数量应该相同,顺序一致
...
注:return只能返回一个值
5.函数原型声明
C语言要求函数先定义后调用,如果自定义函数被放在主调函数的后面,就需要在函数调用前,加上函数原型声明。
函数声明一般格式为:
函数类型 函数名(参数表);
即与函数定义中的第一行(函数首部)相同,并且以分号结束
注:如果在调用函数前,既不定义,也不声明,程序编译时会出错。
练习5-1
(1)使用函数求1到n之和:输入一个正整数n,输出1~n之和。要求定义和调用函数sum(n)求1~n之和。
#include <stdio.h>
int sum(int n);
int main(void){
int n;
printf("n=");
scanf("%d",&n);
printf("%d",sum(n));
return 0;
}
int sum(int n){
int i,sum=0;
for(i=1;i<=n;i++){
sum+=i;
}
return sum;
}
(2)若要计算m~n(m<n)之和,又应该如何定义函数?
#include <stdio.h>
int sum(int m, int n);
int main(void){
int m,n;
printf("m=");
scanf("%d",&m);
printf("n=");
scanf("%d",&n);
printf("%d",sum(m,n));
return 0;
}
int sum(int m, int n){
int i,sum=0;
for(i=m;i<=n;i++){
sum+=i;
}
return sum;
}
练习5-2
使用函数找最大值:输入2个数,输出其中较大的数,要求定义函数max(a, b)找出并返回a、b中较大的数。
#include <stdio.h>
int max(int m, int n);
int main(void){
int m,n;
printf("m=");
scanf("%d",&m);
printf("n=");
scanf("%d",&n);
printf("%d",max(m,n));
return 0;
}
int max(int m, int n){
if(m>n){
return m;
}else{
return n;
}
}
5.2
5.2.2 不返回结果的函数
不返回结果的函数定义:
void 函数名(形参表){
函数实现过程
}
函数类型为void,表示不返回结果,函数体中可以使用没有表达式的return语句,也可以省略return。void类型的函数虽然不直接返回一个值,但它的作用通常以屏幕输出等方式体现。
在不返回结果的函数定义中,void不能省略,否则将会被默认定义成int。
5.2.3 结构化程序设计思想
一. 自顶向下分析问题的方法
- 按自上而下,逐步细化的方法分析问题,按功能将其拆分为几个子问题,如课本P.110页的“学生成绩统计程序的层次结构图”,按照自顶向下的方法分析问题,有助于后续的模块化设计和测试,以及系统的集成
二. 模块化设计
- 一个模块只完成一个指定的功能
- 模块之间只通过参数进行调用
- 一个模块中只有一个入口和出口
- 模块中慎用全局变量
三. 结构化编码主要原则
- 模块化设计后,每一个模块都可以独立编码
- 对变量、函数、常量等命名时,要见名知义,有助于对变量含义或者函数功能的理解
- 在程序中添加必要的注释,增加程序的可读性
- 要有良好的程序层次分明、结构清楚、错落有致和更加清晰
- 程序要清晰易懂,语句结构要简单直接。在不影响功能和性能时,做到结构清晰第一,效率第二
- 程序要有良好的交互性,输入有提示,输出有说明,并且尽量采用统一整齐的输出
练习5-3
字符金字塔:输入一个正整数n和一个字符ch,输出n行由字符ch构成的字符金字塔。
#include <stdio.h>
void tour(int n, char ch);
int main(void){
int n;
char ch;
printf("n=");
scanf("%d",&n);
printf("ch=");
scanf(" %c",&ch);
tour(n,ch);
return 0;
}
void tour(int n, char ch){
int i,j,k;
for(i=1;i<=n;i++){
for(j=1;j<=(n-i);j++){
printf(" ");
}
for(k=1;k<=i;k++){
printf("%c ",ch);
}
printf("\n");
}
}
至于第九行的“scanf(" %c",&ch);”的%号前面为什么要加空格,是因为不加空格运行会出问题,具体原因还木有研究透,研究可参考别人写的博客文章:scanf()函数对字符输入的一些问题_Lemon的博客-CSDN博客_scanf字符输入https://blog.csdn.net/qq_41282102/article/details/80246701
5.3
5.3.2 局部变量和全局变量
1.局部变量
在程序中使用的变量都定义在函数的内部,它们的有效使用范围被局限在所在的函数内。保证了各函数之间的独立性,避免函数之间互相干扰。
C语言将定义在函数内部的变量成为局部变量,形参也是局部变量
除作用于函数的局部变量,C语言还允许定义作用于复合语句中的局部变量,有效范围被局限在复合语句内:
int main(void){
int a=1;
{
int b=2;
...
}
printf("%d", a);
return 0;
}
局部变量一般定义在函数和复合语句的开始处,标准的C语言规定不能定义在中间位置
2.全局变量
局部变量保证了函数的独立性,但程序设计有时还要考虑不同函数之间的数据交流,以及函数的某些统一设置,当一些变量需要被多个函数共同使用时,会受到很大的限制,为了解决多喝函数间的变量共用,C语言中允许定义全局变量。
全局变量格式与局部变量完全一致,但是它定义的位置不同,可以定义在程序的头部,也可以定义在两个函数的中间或者程序尾部,只要在函数外部即可。(但一般情况下把全局变量定义在程序的最前面)
虽然全局变量自由度大,但是在合作开发的程序中,每个人都按照自己的想法使用全局变量,可能产生互相的干扰,一般情况下,尽量使用局部变量和函数参数
5.3.3 变量生存周期和静态局部变量
1.变量生存周期
一般程序而言,计算机都是从主函数开始的,使得main()函数中所有的局部变量一开始就在内存数据区中分配了储存单元。而其他函数在被调用之前,其局部变量并未分配储存单元,只有当函数被调用时,其形参和局部变量才被分配相应的储存单元,一旦函数调用结束返回主调函数,在函数定义的所有形参和局部变量将不复存在,相应的储存单元由系统收回,根据特性,把局部变量称为自动变量,即函数被调用时,系统自动为其局部变量的单元由系统自动回收
变量从定义开始分配储存单元,到运行结束存储单元被回收,整个过程就称为变量生存周期
自动变量的定义形式为:
auto 类型名 变量表;
如:
auto int a, b;
在自动变量定义时,auto可以省略,其形式与以前定义的普通变量完全相同,也就是定义的局部变量都是自动变量
当main()函数调用其他函数时,由于main()还未运行结束,其局部变量仍然存在,还在生存周期中,但由于变量的作用范围,使得main()中的局部变量单元不能再其他函数中使用,只有回到主函数后,那些局部变量才能继续使用。变量的作用范围和生存周期是两个不同的概念,要区分清楚
2.变量储存的内存分布
自动变量和全局变量的生存周期不同,为了便于计算机存储管理,C语言把保存所有变量的数据区分成动态储存区和静态储存区。
它们的管理方式完全不同,动态存储区是使用堆栈来管理的,适合函数动态分配和回收存储单元。而静态储存区相对固定的,管理较简单,它用于存放全局变量和静态变量。
- 静态存储区存放全局变量和静态局部变量
- 动态存储区存放函数中的局部变量(当两个函数中有相同的变量时,由于它们分数不同单位,有各自的内存单元)
3.静态变量
静态局部变量存放在静态储存区,不会像普通局部变量那样因为函数调用结束而被系统回收,它的生存周期会持续到程序结束,由于储存单元被保留,一旦含有静态局部变量的函数被再次调用,则静态局部变量会被重新激活,上一次函数调用后的值依然保存着,可供本次调用继续使用
静态变量定义格式:
static 类型名 变量表
例5-9:
#include <stdio.h>
double fact_s(int n);
int main(void){
int i, n;
printf("Input n:");
scanf("%d", &n);
for(i=1; i<=n; i++){
printf("%3d!=%.0f\n", i, fact_s(i));
}
return 0;
}
double fact_s(int n){
static double f=1;
f=f*n;
return(f);
}
fact_s()函数并没有循环语句,而是考静态变量 f 保存上次函数调用时计算的到的 (n-1)! 的值,再乘上 n ,实现 n! 的计算
静态变量赋初值只在函数第一次调用时起作用,若没有赋初值,系统将自动赋值为 0
练习5-4
将例5-9中的静态变量 f 定义为普通局部变量,还能实现计算 n! 吗?请上级检验。若把 f 换成全局变量又会如何?
答:当定义为普通局部变量时,每次调用函数时变量 f 都会是1,既无法实现计算 n! ,若将 f 换成全局变量,变量 f 的值不会随函数的调用而使它的值变为1,所以当变量 f 换成全局变量时,可以实现 n! 的计算
习题5
程序设计题1
使用函数计算分段函数的值:输入x,计算并输出下列分段函数f(x)的值,要求定义和调用函数sign(x)实现该分段函数。
#include <stdio.h>
int sign(int num);
int main(void){
int x;
printf("x=");
scanf("%d",&x);
printf("%d",sign(x));
return 0;
}
int sign(int num){
if(num>0){
return 1;
}else if(num==0){
return 0;
}else{
return -1;
}
}
程序设计题2
使用函数求奇数和:输入一批正整数(以零或者负数为结束标志),求其中的奇数和。要求定义和调用函数even(n)判断的奇偶性,当n为偶数时返回1,否则返回0
#include <stdio.h>
int even(int n);
int main(void){
int num,sum=0;
printf("输入整数:\n");
do{
scanf("%d",&num);
if(even(num)==0){
sum+=num;
}
} while(num>0);
printf("奇数和为:%d",sum);
return 0;
}
int even(int n){
if(n%2==0){
return 1;
}else{
return 0;
}
}
程序设计题3
使用函数计算两点间的距离:给定平面任意两点的坐标(x1, y1)和(x2, y2),求这两点之间的距离(保留两位小数)。要求定义和调用函数dist(x1, y1, x2, y2)计算两点间的距离
#include <stdio.h>
#include <math.h>
double dist(double x1, double y1, double x2, double y2);
int main(void){
double x1,y1,x2,y2;
printf("输入第一个坐标:");
scanf("%lf %lf", &x1, &y1);
printf("输入第二个坐标:");
scanf("%lf %lf", &x2, &y2);
printf("距离为:%.2f",dist(x1,y1,x2,y2));
return 0;
}
double dist(double x1, double y1, double x2, double y2){
double s;
s=sqrt(pow((x1-x2),2)+pow((y1-y2),2));
return s;
}
程序设计题4
利用函数计算素数个数并求和:输入两个正整数m和n(1<=m,n<=500),统计并输出m和n之间的素数的个数以及这些素数的和。要求定义函数prime(m)判断m是否为素数。
#include <stdio.h>
int prime(int m);
int main(void){
int i,m,n,num=0,sum=0;
printf("m=");
scanf("%d",&m);
printf("n=");
scanf("%d",&n);
for(i=m;i<=n;i++){
if(prime(i)==1){
num++;
sum+=i;
}
}
printf("素数的个数:%d 素数和:%d",num,sum);
return 0;
}
int prime(int m){
int i,result=1;
if(m==2){ //特例,2为素数
result=1;
}else if(m!=1){
for(i=2;i<m;i++){
if(m%i==0){
result=0;
break;
}
}
}else{ //特例,1不为素数
result=0;
}
return result;
}
程序设计题5
使用函数统计指定数字的个数:读入一个整数,统计并输出该数中“2”的个数,要求定义并调用函数countdigit(number, digit),它的功能是统计整数number中的数字digit的个数。
#include <stdio.h>
#include <math.h>
int countdigit(int number, int digit);
int main(void){
int number,digit;
printf("number=");
scanf("%d",&number);
printf("digit=");
scanf("%d",&digit);
printf("%d 中共有 %d 个 %d",number,countdigit(number,digit),digit);
return 0;
}
int countdigit(int number, int digit){
int n=0,remainder,i=0,p=1,temp,sum=0,num;
do{ //判断数字的位数
remainder=number/p;
if(remainder!=0){
n++;
}
i++;
p=pow(10,i);
} while(remainder!=0);
temp=number;
for(i=n;i>0;i--){ //将每一位数都提取判断
num=temp/pow(10,i-1);
temp=temp-num*pow(10,i-1);
if(num==digit){
sum++;
}
}
return sum;
}
(感觉可以再优化优化的...
程序设计题6
使用函数输出水仙花数:输入两个正整数m和n(1≤m,n≤1000),输出m~n之间的所有满足各位数字的立方等于它本身的数,要求定义并调用函数is(number)判断number的各位数字的立方和是否等于它本身。
#include <stdio.h>
#include <math.h>
int is(int number);
int main(void){
int m,n,i;
printf("m=");
scanf("%d",&m);
printf("n=");
scanf("%d",&n);
if(m>=1&&n<=1000&&m<=n){
for(i=m;i<=n;i++){
if(is(i)==1){
printf("%d\n",i);
}
}
}else{
printf("Invalid.");
}
return 0;
}
int is(int number){
int n=0,remainder,i=0,p=1,temp,sum=0,num;
do{ //判断数字的位数
remainder=number/p;
if(remainder!=0){
n++;
}
i++;
p=pow(10,i);
} while(remainder!=0);
temp=number;
for(i=n;i>0;i--){ //将每一位数都提取判断
num=temp/pow(10,i-1);
temp=temp-num*pow(10,i-1);
sum+=pow(num,3);
}
if(sum==number){
return 1;
}else{
return 0;
}
}
程序设计题7
使用函数求余弦函数的近似值:输入精度e,用下列公式求cosx的近似值,精确到最后一项的绝对值小于e。要求定义和调用函数funcos(e,x)求余弦函数的近似值。
#include <stdio.h>
#include <math.h>
double funcos(double e,double x);
int main(void){
double e,x;
printf("e=");
scanf("%lf",&e);
printf("x=");
scanf("%lf",&x);
printf("cos(%f) ≈ %f",x,funcos(e,x));
return 0;
}
double funcos(double e,double x){
int i=0,j,flag=1;
double sum=0,last,fact;
do{
fact=1;
for(j=1;j<=i;j++){ //求阶乘
fact*=j;
}
last=pow(x,i)/fact;
sum+=flag*last;
flag=-flag;
i+=2;
} while(last>=e);
return sum;
}
程序设计题8
输入一个正整数n,输出n行空心的数字金字塔。要求定义和调用函数hollow_pyramid(n)输出n行空心的数字金字塔。当n=5时,5行空心的金字塔如下所示:
#include <stdio.h>
void hollow_pyramid(int n);
int main(void){
int n;
printf("n=");
scanf("%d",&n);
hollow_pyramid(n);
return 0;
}
void hollow_pyramid(int n){
int i,j;
for(i=1;i<=n;i++){
for(j=n-i;j>0;j--){ //负责每一行的空格
printf(" ");
}
if(i==1){ //第一行放一个
printf("%d\n",i);
}else if(i!=n){ //中间行
printf("%d",i);
for(j=1;j<=2*(i-1)-1;j++){
printf(" ");
}
printf("%d\n",i);
}else{ //最后一行
for(j=1;j<=2*(i-1)+1;j++){
printf("%d",i);
}
}
}
}
- 第5章结束 -
(多学习,多练习,多注意细节)
整理自书籍《C语言程序设计》
如有错误,欢迎纠正!