C语言基础(六)函数


系列合集 初窥C语言

七、函数

7.1 函数及函数定义的一般形式

7.1.1 函数概述

一个C程序可由一个主函数和若干个函数构成。由主函数调用其他函数,其他函数也可以互相调用。同一个函数可以被一个或多个函数依次调用任意多次。

7.1.2 函数定义的一般形式

无参函数的定义形式:
类型标识符 函数名()
{ 声明部分;
语句;}

有参函数定义的一般形式:
类型标识符 函数名(形式参数列表)
{ 声明部分;
语句;}

可以有“空函数”
类型说明符 函数名(){}

7.1.3 函数说明

(1)一个源文件由一个或多个函数组成。一个源程序文件是一个编译单位,即以源程序为单位进行编译而不是以函数为单位进行编译。
(2)一个C程序由一个或多个源程序文件组成。对较大的程序,一般不希望全放在一个文件中,而将函数和其他内容(如预定义)分别放在若干个源文件中,再由若干源文件组成一个C程序。这样分别编写,分别编译,提高调试效率。一个源程序可以为多个C程序公用。
(3)C程序的执行从main函数开始,调用其他函数后流程返回到main函数,在main函数中结束整个程序的运行。main函数是系统定义的。
(4)所有函数都是平行的,即在定义函数时是相互独立的,一个函数并不从属于另一函数,即函数不能嵌套定义(这是和PASCAL不同的)。函数间可以互相调用,但不能调用main函数。
(5)从用户使用的角度看,函数有两种:
1)标准函数,即库函数。
2)用户自己定义的函数。
(6)从函数的形式看,函数分两类:
1)无参函数。在调用无参函数时,主调函数未传送数据给被调函数,一般用来执行指定的一组操作。无参函数可以 带回或不带回函数值,但一般以不带回函数值居多。
2)有参函数。在调用函数时,在主调函数和被调函数之间有数据传递。也就是说,主调函数可以将数据传输给被调函数使用,被调函数中的数据也可以返回给主调函数使用。

7.2 函数参数和函数的值

7.2.1 形式参数和实际参数

在定义函数时,函数名后面括号中的变量名称为“形式参数”(简称“形参”),在主调函数中调用一个函数时,函数名后面括号中的参数(可以是一个表达式)被称为“实际参数”。
说明:
(1)在定义函数中指定的形参,在未出现函数调用时,它们并不占内存中的存储单元。只有在发生函数调用时,函数中的形参才会被分配内存单元。在调用结束后,形参所占的内存单元也被释放。
(2)实参可以是常量、变量或表达式。在调用时将实参的值赋给形参。
(3)在被定义的函数中,必须指定形参的类型。
(4)实参与形参的类型应相同或赋值兼容。
(5)C语言规定,实参变量对形参变量的数据传递是“值传递”,即单向传递,只由实参传给形参,而不能由形参传回来给实参。在内存中,实参单元与形参单元是不同的单元。

7.2.2 函数的返回值:

通过函数调用使主调函数能得到一个确定的值,这就是函数的返回值。
说明:
(1)函数的值只能通过return语句返回主调函数。
return(表达式):该语句的功能是计算表达式的值,并返回给主调函数。在函数中允许有多个return语句,但遇到第一个return就返回主调函数,因此只能返回一个函数值。一般用在选择结构中,每次调用只能有一个return语句被执行。
(2)函数值的类型和函数定义中函数的类型应保持一致。
(3)如函数值的类型和return语句中表达式的值不一致,以函数类型为准。
(4)如果被调用函数中没有return语句,并不带回一个确定的、用户所希望得到的函数值,但实际上,函数并不是不带回函数值,而只是不带回有用的值,带回的是一个不确定的值。
(5)为了明确表示“不带回值”,可以用“void”定义“无类型”(或称空类型)。

7.3 函数的调用

函数调用的一般形式:
2函数名(实参列表);
调用无参函数,“实参列表”可以没有;包括多个实参,各参数间用逗号隔开。
函数调用的方式:函数语句、函数表达式、函数参数
对被调用函数的声明和函数原型:
(1)函数调用的条件
(2)函数原型
函数调用的条件:
1)被调用的函数必须是已经存在的函数(是库函数或用户自己定义的函数)。
2)如果使用库函数,一般还应该在头文件开头用#include命令将调用有关库函数时所需要用到的信息“包含”本文件中来。
3)如使用用户自己定义的函数,而且该函数与调用函数在同一文件中,一般应在主调函数对被调用的函数做声明。

7.3.1 函数的嵌套调用

1)C语言的函数定义都是相互平行、独立的,也就是说在定义函数时,一个函数内部不能定义另一个函数。
2)C语言不可以嵌套定义,但可以嵌套调用函数。
在这里插入图片描述

7.3.2 函数的递归调用

递归调用:在调用一个函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用。
在这里插入图片描述在这里插入图片描述

7.4 函数的数组参数

7.4.1 数组元素做函数实参

有两个数组a,b,各有10个元素,将它们对应地逐个相比(即a[0]和b[0]相比对…)。如果a数组中的元素大于b数组中的相应元素的数目多与b数组中元素大于a数组中相应元素的数目(如a[i] > b[i]6次,b[i] > a[i]3次,i为每次不同的值),则认为a数组大于b数组,并分别统计出两个数组相应元素大于,等于,小于的次数。

#include <stdio.h>
int main(){
	int large(int x, int y);
	int a[10],b[10],i,n = 0,m = 0,k = 0;
	printf("enter array \n");
	for(i = 0; i < 10; i++){
		scanf("%d",&a[i]);
	}
	for(i = 0; i < 10; i++){
		scanf("%d",&b[i]);
	}
	printf("\n");
	for(i = 0; i < 10; i++){
		if(large(a[i],b[i]) == 1) n = n + 1;
		else if (large(a[i],b[i]) == 0) m = m + 1;
		else k = k + 1;
	}
	printf("a[i] > b[i] %d times \n a[i] = b[i] %d times \n a[i] < b[i] %d times \n", n, m, k );
	if(n > k) printf("array a is larger than array b \n");
	else if(n < k) printf("array a is smaller than array b \n");
	else printf("array a is equal to array b");	
}
int large(int x, int y){
	int flag;
	if(x > y) flag = 1;
	else if(x < y) flag = -1;
	else flag = 0;
	return(flag);
}

7.4.2 数组名做函数参数数组

有一个一维数组score,内放10个学生成绩,求平均成绩

#include <stdio.h>
float average(float array[10]){
	int i;
	float aver, sum = array[0];
	for(i = 1; i < 10; i++){
		sum = sum + array[i];
	} 
	aver = sum / 10;
	return(aver);
}
int main(){
	float score[10],aver;
	int i;
	printf("input 10 scores: \n");
	for(i = 0; i < 10; i++){
		scanf("%f",&score[i]);
	}
	printf("\n");
	aver = average(score);
	printf("average score is %.2f",aver);
}

用数组与用数组元素名做函数参数不同点
(1)用数组元素做实参时,对数组元素的处理按普通变量对待。用数组名做函数参数时,则要求形参和对应的实参都必须是类型相同的数组,都必须有明确的数组说明。当形参和实参二者不一致时,就会发生错误。
(2)在普通变量或数组元素做参数时,形参变量和实参变量是有编译系统分配的两个不同的内存单元。在函数调用时发生的值传递是把实参的值赋给形参变量。在数组名做函数参数时,不进行值的传送,不是实参数组的每一个元素的值都赋给形参数组的各个元素,是把实参数组的首地址赋给形参数组名。

7.4.3 用多维数组名做函数参数

多维数组元素与一维数组一样,可以看作一个变量,所以在调用函数时可以作为实参,进行值的传递。
用多维数组名作为函数参数传递的是数组首元素的地址,要求形参是相同类型的同维数组。这里,形参是二维数组时,第二维数组的大小(长度)必须指明,而第一维的大小(长度)可以不指名。
如:

int array[3][10]
int array[][10]
// int array[][]和int [3][]是错的

例如:求出3*4的矩阵(二维数组)中最大的元素

#include <stdio.h>
int max_value(int array[][4]){
	int i, j, k, max;
	max = array[0][0];
	for(i = 0; i < 3;i++){
		for(j = 0; j < 4; j++){
		if(array[i][j] > max)
		max = array[i][j];		 
		}
	}
	return max;
}
int main() {
	int a[3][4] = { {1,2,3,4}, {5,6,7,8}, {9,0,1,2}};
	printf("maxvalue is %d \n" , max_value(a));
}

7.5 函数内的变量

7.5.1 局部变量和全局变量

(1)局部变量:在一个函数内部定义的变量,是内部变量,只在本函数范围内有效。

说明:
1)局部变量只在定义该变量的函数的内部才可以访问,才是有效的。
2)不同的函数内部定义的变量可以取相同的名字,由于1)的原因,访问时才不会发生混淆。
3)函数的形式参数也是局部变量。
4)在函数内部,可以在复合语句中定义局部变量,这些局部变量只在该复合语句内部可见、有效。

(2)全局变量:全局变量可以为本文件中其他函数所共用。其有效范围是从定义变量的位置开始到本文件结束。

说明:
1)设全局变量的作用是增加了函数间数据联系的渠道。文件中各个函数都能访问全局变量。
2)全局变量不应设置过多,以降低函数之间相互影响,达到模块化设计。
3)若全局变量与局部变量同名,则在局部变量作用的范围内,局部变量有效(即局部优先)。

7.5.2 变量的动态存储和静态存储

静态存储方式中,程序运行期间为变量分配固定的存储空间。
动态存储方式中,程序运行期间根据需要为变量动态分配存储空间。

内存中供用户使用的存储空间大致分为三块:
1)程序区
程序区存放程序的机器码,数据存放在静态存储区和动态存储区。
2)静态存储区
静态存储区存放全局变量和静态局部变量。
3)动态存储区
动态存储区存放自动局部变量,形式参数,函数调用时的被保护的返回地址(断点)和需要保护的现场信息。

静态存储区:静态局部变量在静态存储区内分配存储单元,在程序整个运行期间都不释放。如在定义static局部变量时不赋初值,编译时自动赋初值0(对数值型变量)或空字符(对字符型变量)。
动态存储区:自动变量占动态存储空间,函数调用结束就释放。如果不赋初值则它的值是一个不确定的值。

7.5.3 auto

变量有两个属性: 数据类型 存储类别
变量的存储类别指的是变量在内存中存放的方式————静态存储还是动态存储。共分为四种: auto,static,register,extern。
auto变量:在函数中的形参和在函数中定义的变量,都属于此类。在调用该函数时系统临时为它们分配存储空间,在调用该函数结束时自动释放这些存储空间。
实际上,关键字“auto”可以省略,则隐含确定为“自动存储类别”,属于动态存储方式。

7.5.4 static

(1)static声明局部变量
变量所在函数结束后,该变量所在的存储单元不释放,在下一次该函数调用时,该变量值保持上一次调用结束的状态。
(2)static声明外部变量
有时在程序设计中希望某些外部变量只限于被本文件引用,而不被其他文件引用,这时可以在定义外部变量时加static声明。
这并不意味着加了static才是静态变量(存放在静态存储区中),不加static是动态存储(存放在动态存储区),这两种形式的外部变量都是静态存储方式,只是作用范围不同。
(3)static声明一个变量的作用
1)局部变量用static声明,则为该变量分配的空间在整个程序执行期间始终存在。
2)全局变量用static声明,则该变量的作用域只限于本文件模块(即被声明的文件中)。

7.5.5 register

如果有一些变量使用频繁,可能会为存储变量的值花不少时间。为提高执行效率,C语言允许将局部变量的值存放在CPU内的寄存器中,需要时直接从寄存器取出参数来运算,不必再到内存中去存取。这种变量叫“寄存器变量”(register)。
1)只有局部自动变量和形式参数可以作为寄存器变量
2)一个计算机系统中的寄存器数目是有限的,不能定义任意多个寄存器变量
3)局部静态变量不能定义为寄存器变量
如:register static int a;是错误的

7.5.6 extern

用extern声明外部变量
外部变量(即全局变量)是在函数的外部定义的,它的作用域为从定义变量的位置开始到本文件结束。在此作用域内,全局变量可以为程序中各个函数所使用,编译时将外部变量分配到静态存储区。
如果在定义点之前的函数要引用外部变量,则在引用之前要用关键字“extern”对该变量作“外部变量声明”。

小结:
存储类别小结

7.6 函数的声明和定义

7.6.1 函数声明与定义的区别

“定义”是对函数功能的确立,包括指定函数名,函数值类型,形参及类型等,是一个完整,独立的函数单位。
“声明”是把函数的名字,函数类型和形参的类型、个数以及顺序通知编译系统,以便在调用该函数时系统按此进行对照检查。
1)如在函数调用之前,没有对函数做声明,则编译系统会把第一次遇到的该函数形式(函数定义或函数调用)作为函数的声明,并将函数类型默认为int型,出现编译警告(warning),结果不可预料。
2)如果被调用函数的定义出现在主调函数之前,可不必声明。
3)如果已在所有函数定义之前,在函数外部做了函数声明,则在各个主调函数中不必对所调函数再次做声明。
4)函数声明是放在声明部分,函数定义是一个独立的模块。

7.6.2 对变量的声明和定义

(1)在声明部分出现的变量的两种情况
需要建立存储空间(如: int a; )定义性声明
不需要建立存储空间(如: extern a;)引用性说明
(2)外部变量的定义和声明
外部变量的定义只一次,位置在所有函数之外,而同一文件的声明可以有许多次,位置在函数内。外部变量的初始化只能在“定义”时进行。

7.7 内部函数与外部函数

根据函数能否被其他文件调用,将函数分为内部函数和外部函数。
内部函数:static 类型标识符 函数名(参数列表)
外部函数:
(1)在定义函数时,如果在函数首部的最左端冠以关键字extern,则表示此函数是外部函数,可供其他文件调用。
(2)在需要调用此函数的文件中,用extern声明所用的函数是外部函数。
例如:有一个字符串,其中有若干字符,今输入一个字符,要求程序将该字符从字符串中删去。用外部函数实现。

在文件file1.c中定义主函数

#include <stdio.h>

int main() {
	extern void enter_string (char *);
	extern void delete_string (char *, char);
	extern void print_string (char *);
	char c,str[80];
	enter_string (str);
	scanf("%c", &c);
	delete_string (str,c);
	print_string (str);
}

其他文件中定义外部函数

#include <stdio.h>
 extern void enter_string(char *s){
	gets(s);
 }

缺省extern的函数默认为外部函数

 void delete_string(char *c, char ch){
 	int i, j;
 	for(i = j = 0; s[i]; i++){
 		if(s[i] != ch) s[j++] = s[i];
	 }
	 s[j] = '\0';
 }
  void print_string(char *s){
 	printf("%s", s);
 }

7.8 函数应用举例

1.用递归方法求n!

#include <stdio.h>
float fac(int n){
	float f;
	if (n < 0) printf("n < 0, error");
	else if(n == 0 || n == 1) f = 1;
	else f = fac(n - 1) * n;
	return(f);
}

int main(){
	int n;
	float y;
	printf("input an integer number:");
	scanf("%d", &n);
	y = fac(n);
	printf("%d!= %10.0f", n, y);
}

2.Hanoi(汉诺)塔问题:有三个柱子A,B,C。A柱子上有64个盘子。盘子大小不等,大的在下,小的在上。想把这64个盘子从A柱移到C柱,但每次只允许移动一个盘子,且移动过程中三个柱子始终保持大盘在下,小盘在上。

#include <stdio.h>
void move(char x, char y){
	printf("%c ---> %c\n", x, y);
}
void hanoi(int n, char first, char second, char third){ //将n个盘借助second从first移到third 
	if(n == 1) move (first, third);
	else{
		hanoi(n - 1, first, third, second);
		move(first, third);
		hanoi(n -1, second, first, third);
	}
}
int main(){
	int m;
	printf("input the number of diskes:");
	scanf("%d",&m);
	printf("the step to moveing %3d diskes : \n", m);
	hanoi(m, 'A','B','C');
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是10道C语言函数基础练习题的答案: 1. 交换两个变量的值 ```c void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } ``` 2. 计算一个数的平方 ```c int square(int num) { return num * num; } ``` 3. 判断一个数是否为素数 ```c int isPrime(int num) { int i; for (i = 2; i <= num / 2; i++) { if (num % i == 0) { return 0; } } return 1; } ``` 4. 计算一个数的阶乘 ```c int factorial(int num) { int i, result = 1; for (i = 1; i <= num; i++) { result *= i; } return result; } ``` 5. 计算两个数的最大公约数 ```c int gcd(int a, int b) { int temp; while (b != 0) { temp = b; b = a % b; a = temp; } return a; } ``` 6. 计算两个数的最小公倍数 ```c int lcm(int a, int b) { int temp = gcd(a, b); return a * b / temp; } ``` 7. 判断一个数是否为回文数 ```c int isPalindrome(int num) { int temp = num, reverse = 0; while (temp != 0) { reverse = reverse * 10 + temp % 10; temp /= 10; } return num == reverse; } ``` 8. 判断一个数是否为 Armstrong 数 ```c int isArmstrong(int num) { int temp = num, digits = 0, sum = 0; while (temp != 0) { digits++; temp /= 10; } temp = num; while (temp != 0) { sum += pow(temp % 10, digits); temp /= 10; } return num == sum; } ``` 9. 将一个十进制数转换为二进制数 ```c void decimalToBinary(int num) { int binary[32], i = 0; while (num > 0) { binary[i] = num % 2; num /= 2; i++; } for (int j = i - 1; j >= 0; j--) { printf("%d", binary[j]); } } ``` 10. 计算三角形的面积 ```c double triangleArea(double a, double b, double c) { double s = (a + b + c) / 2; double area = sqrt(s * (s - a) * (s - b) * (s - c)); return area; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Rpk712

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

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

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

打赏作者

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

抵扣说明:

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

余额充值