【C语言入门】笔记六 (函数)

第五章 函数

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页的“学生成绩统计程序的层次结构图”,按照自顶向下的方法分析问题,有助于后续的模块化设计和测试,以及系统的集成

二. 模块化设计

  1. 一个模块只完成一个指定的功能
  2. 模块之间只通过参数进行调用
  3. 一个模块中只有一个入口和出口
  4. 模块中慎用全局变量

三. 结构化编码主要原则

  1. 模块化设计后,每一个模块都可以独立编码
  2. 对变量、函数、常量等命名时,要见名知义,有助于对变量含义或者函数功能的理解
  3. 在程序中添加必要的注释,增加程序的可读性
  4. 要有良好的程序层次分明、结构清楚、错落有致和更加清晰
  5. 程序要清晰易懂,语句结构要简单直接。在不影响功能和性能时,做到结构清晰第一,效率第二
  6. 程序要有良好的交互性,输入有提示,输出有说明,并且尽量采用统一整齐的输出

练习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语言把保存所有变量的数据区分成动态储存区静态储存区

它们的管理方式完全不同,动态存储区是使用堆栈来管理的,适合函数动态分配回收存储单元。而静态储存区相对固定的,管理较简单,它用于存放全局变量和静态变量。

  1. 静态存储区存放全局变量静态局部变量
  2. 动态存储区存放函数中的局部变量(当两个函数中有相同的变量时,由于它们分数不同单位,有各自的内存单元)

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()函数并没有循环语句,而是考静态变量 保存上次函数调用时计算的到的 (n-1)! 的值,再乘上 ,实现 n! 的计算

静态变量赋初值只在函数第一次调用时起作用,若没有赋初值,系统将自动赋值为 0 

练习5-4

将例5-9中的静态变量 f 定义为普通局部变量,还能实现计算 n! 吗?请上级检验。若把 f 换成全局变量又会如何?

答:当定义为普通局部变量时,每次调用函数时变量 f 都会是1,既无法实现计算 n! ,若将 f 换成全局变量,变量 f 的值不会随函数的调用而使它的值变为1,所以当变量 f 换成全局变量时,可以实现 n! 的计算

习题5

程序设计题1

使用函数计算分段函数的值:输入x,计算并输出下列分段函数f(x)的值,要求定义和调用函数sign(x)实现该分段函数。

f(x)=\left\{\begin{matrix} 1 & x>0 & \\ 0 & x=0 & \\ -1 & x<0 & \end{matrix}\right.

#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

利用函数计算素数个数并求和:输入两个正整数mn1<=m,n<=500),统计并输出mn之间的素数的个数以及这些素数的和。要求定义函数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

使用函数输出水仙花数:输入两个正整数mn1≤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)求余弦函数的近似值。

cosx=\frac{x^{0}}{0!}-\frac{x^{2}}{2!}+\frac{x^{4}}{4!}-\frac{x^{6}}{6!}+...

#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语言程序设计》

如有错误,欢迎纠正!

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值