为什么使用函数
使main函数变短,容易阅读。需要用的功能不用重复敲。(函数在OC里称为方法)
函数的定义
定义函数的方法
定义无参函数
类型名 函数名(void(可有可无)) {
函数体
}
定义有参函数,如:
int max(int x, int y) {
int z;
z = x > y ? x : y;
return z;
}
定义空函数
void dummy() {
}
函数调用
函数调用的形式
函数调用语句
把函数调用单独作为一个语句,如:printStar
函数调用出现在另一个表达式中,
如:c = 2 * max(a, b);
函数参数
函数调用时作为另一个函数调用时的实参,如:
m = max(c, max(a, b));
printf("%d", max(a, b));
printf为系统函数
函数调用时的数据传递
形参和实参
形参:自定义函数中的参数
实参:main函数调用自定义函数中的参数
实参和形参间的数据传递
实参——>形参,在函数调用过程中发生的实参和形参间的数据传递,常成为“虚实结合”。
例 输入两个整数,输出其中的较大者
说明:
a.实参可以是常量,变量或者表达式,但要求它们有确定的值
b.形参与实参的类型应相同或赋值兼容
int max(int x, int y) {
int z;
z = x > y ? x : y;
return z;
}
int main(int argc, const char * argv[]) {
printf("%d", max(3, 4));
return 0;
}
函数调用的过程
- 形参在未调用时,并不占内存的存储单元
- 实参对应的值传给形参
在执行MAX函数期间,由于形参已经有值,就可以利用形参进行有关运算, 通过return将函数值带回到主调函数,如果函数不需要返回值,则不需要return语句- 结束调用,形参单元被释放
注意:实参单元仍保留并维持原值,并没有改变。如果在执行一个被调用函数时,形参的值发生改变,不会改变主调函数实参的值,实参向形参的传递是“值传递”,单向传递,只能实参到形参,而不能形参到实参,因为实参和形参占用存储单元不同,实参无法得到实参的值。
函数的返回值
- 函数的返回值是通过函数中的return语句获得的
- 函数值类型。既然函数有返回值,这个值当然属于某一个确定的类型,应当在定义函数时指定函数值得类型
- 在定义函数时指定的函数类型一般应和return语句中表达式的值一致
对被调用的函数的声明和函数原型
函数的首行称为函数原型, 通常函数原型声明函数
例 输入两个实数,用一个函数求出它们的和
float add(float x, float y);
int main(int argc, const char * argv[]) {
printf("%.2f", add(2.6, add(2.4, add(2.6, 2.4))));
return 0;
}
float add(float x, float y) {
float z;
z = x + y;
return z;
}
函数的嵌套调用
例 输入四个整数,找出其中最大数,用函数的嵌套调用来处理
int maxTwoNum(int a, int b);
int maxFourNum(int a, int b, int c, int d);
int main(int argc, const char * argv[]) {
int a = 0, b = 0, c = 0, d = 0;
printf("Please input 4 nums\n");
scanf("%d%d%d%d", &a, &b, &c, &d);
printf("MAX num is %d\n", maxFourNum(a, b, c, d));
return 0;
}
int maxTwoNum(int a, int b) {
return a > b ? a : b;
}
int maxFourNum(int a, int b, int c, int d) {
return maxTwoNum(maxTwoNum(maxTwoNum(a, b), c), d);
}
递归
在调用函数的过程中又出现直接或间接地调用该函数本身,称为函数的递归调用
例 有5个学生坐在一起,问第五个学生多少岁,他说比第 4个学生大2岁。问第4个学生,他说比第三个学生大2岁。问第3个学生,他说比第2个学生大2岁。问第二个学生,他说比第1个学生大2岁。最后问第一个学生,他说10岁。请问第五个学生多大。
int nianling(int x);
int main(int argc, const char * argv[]) {
/*
解题思路:
age(5) = age(4) + 2;
age(4) = age(3) + 2;
age(3) = age(2) + 2;
age(2) = age(1) + 2;
age(1) = 10;
用数学公式表示如下:
age(n) = 10; (n = 1)
age(n) = age(n - 1) + 2; (n > 1)
*/
int n = 0;
printf("请输入第n个学生:\n");
scanf("%d", &n);
printf("第%d个学生的年龄是%d\n", n, nianling(n));
}
int nianling(int x) { //x是第x个学生
int a;
if (x == 1) {
a = 10;
} else {
a = nianling(x - 1) + 2;
}
return a;
}
例 用递归求n!
int factorial(int n);
int main(int argc, const char * argv[]) {
/*
n! = 1; (n = 1)
n! = n * (n - 1); (n > 1)
*/
int n;
printf("Please input a num:\n");
scanf("%d", &n);
printf("The result is %d\n", factorial(n));
return 0;
}
int factorial(int n) {
int f;
if (n == 1) {
f = 1;
} else {
f = n * factorial(n - 1);
}
return f;
}
数组作为函数参数
数组元素作函数实参
数组元素可以用作函数实参,不能用作形参。因为形参是在函数被调用时临时分配存储单元的,不可能为一个数组元素单独分配存储单元。在用数组元素作函数参数时,把实参的值传给形参,是“值传递”方式,单向传递。
例 输入十个数,要求输出其中值最大的元素和该数是第几个数。
int max(int x, int y);
int main(int argc, const char * argv[]) {
/*
解题思路:可以定义一个数组a,长度为10,用来存放10个数。设计一个Max函数,用来求两数中的大者。在主函数中定义一个变量m,m的初值为a[0],每次调用m后的返回值存放在m中。依次将数组元素a[1]到[9]与m比较,最后得到的m值就是10个数中的最大者。
*/
int a[10] = {18, 19, 1, 27, 6, 45, 99, 17, 65, 88};
int m = a[0];
int n = 0;
for (int i = 0; i < 10; i++) {
if (max(m, a[i]) > m) {
m = max(m, a[i]);
n = i;
}
}
printf("%d %d", m, n);
return 0;
}
int max(int x, int y) {
return x > y ? x : y;
}
数组名作函数参数
除了可以用数组元素作为函数参数外,还可以用数组名作函数参数(包括实参和形参)
注意:用数组元素作实参时,向形参变量传递的是数组元素的值,而用数组名作函数实参时,向形参(数组名或指针变量)传递的是数组首元素地址。
例 有一个一维数组score,内放10个学生成绩,求平均成绩
float average(int array[10]);
int main(int argc, const char * argv[]) {
/*
解题思路:用一个函数average求平均成绩,不用数组元素作为函数实参,而用数组名作为函数实参,形参也用数组名,在average中引用各数组元素,求平均成绩并返回main函数。
*/
int array[10], i = 0;
for (i = 0; i < 10; i++) {
array[i] = arc4random()%15;
printf("%d ", array[i]);
}
printf("\n");
printf("%0.2f", average(array));
return 0;
}
float average(int array[10]) {
int i = 0;
float average = 0.0f, sum = 0.0f;
for (i = 0; i < 10; i++) {
sum += array[i];
}
average = sum / 10;
return average;
}
例 有两个班,分别有35名和30名学生,调用一个average函数,分别求这两个班平均成绩
float average(float array[], int n);
int main(int argc, const char * argv[]) {
float class1[30] = {0};
float class2[35] = {0};
for (int i = 0; i < 30; i++) {
class1[i] = arc4random()%51 + 50;
printf("%0.2f ", class1[i]);
}
printf("\n");
for (int i = 0; i < 35; i++) {
class2[i] = arc4random()%51 + 50;
printf("%0.2f ", class1[i]);
}
printf("\n");
printf("班级1平均成绩是:%0.2f\n", average(class1, 30));
printf("班级2平均成绩是:%0.2f\n", average(class2, 35));
return 0;
}
float average(float array[], int n) {
float average = 0.0f;
float sum = 0.0f;
for (int i = 0; i < n; i++) {
sum = sum + array[i];
}
average = sum / n;
return average;
}
例 用选择法将数组中十个整数进行排序
选择法就是先将10个数中最小的数与a[0]对换,再将a[0]~a[9]中最小的数与a[1]对换。(冒泡排序)
void bubbleSort(int array[], int n);
int main(int argc, const char * argv[]) {
int a[10] = {1, 5, 7, 0, 9, 3, 6, 4, 2, 8};
bubbleSort(a, 10);
for (int i = 0; i < 10; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
void bubbleSort(int array[], int n) {
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (array[j] > array[j + 1]) {
int t = array[j];
array[j] = array[j + 1];
array[j + 1] = t;
}
}
}
}
多维数组名作函数参数
注意:形参数组的第一维大小可以省略,第二维不可以,并且要和实参的相同。在主函数调用maxvalue函数时,把实参第一行起始地址传给arr,因此a与arr起始地址相同,由于两个数组列数相同,因此arr数组第二行起始地址与a也相同。arr与a同占一个存储单元,它们具有同一个值。实际上,arr就是a,在函数中对arr操作就是对a操作
例 有一个3*4的矩阵,求所有元素中最大值
int maxValue(int a[][4]);
int main(int argc, const char * argv[]){
int a[3][4] = {{1, 3, 5, 7}, {2, 4, 6, 8}, {15, 17, 34, 12}};
printf("MAX: %d", maxValue(a));
return 0;
}
int maxValue(int arr[][4]) {
int i, j, max;
max = arr[0][0];
for (i = 0; i < 3; i++) {
for (j = 0; j < 4; j++) {
if (arr[i][j] > max) {
max = arr[i][j];
}
}
}
return max;
}
局部变量和全局变量
局部变量
- 主函数中定义的函数只在主函数中有效,不能使整个文件或程序中有效,其他函数定义的变量主函数也不能用
- 不同函数中可以使用同名变量
- 形参也是局部变量
- 在一个函数内部,可以在复合语句中定义变量,这些变量只在本复合语句中有效,这种复合语句也称为“分程序”或“程序块”
全局变量
在函数外定义的称为全局变量,也称外部变量由于函数的调用只能带回一个函数返回值,因此有时可以利用全局变量来对增加函数间的联系渠道,通过函数调用能得到一个以上的值
变量的存储方式和生存期
动态存储方式和静态存储方式
内存空间可以分为三部分:程序区、静态存储区、动态存储区
全局变量全部存放在静态存储区
动态存储区存放以下数据:
- 形参
- 没有用static声明的变量
- 函数调用时的现场保护和返回地址等
局部变量的存储类别
- 自动变量(auto变量)
在调用函数时,自动分配存储空间,函数调用结束,自动释放。
auto int a = 0;- 静态局部变量(static局部变量)
有时希望函数中的局部变量的值在调用后不消失并保留(上一次调用结束后的值),这时就指定其为静态局部变量,用关键字static声明。
static a = 0;
内部函数和外部函数
内部函数
如果一个函数只能被本文件中其他函数所调用,它称为内部函数
在定义内部函数时,在函数名和函数类型前面加static,即:
static 类型名 函数名(形参表):
static int fun(int a, int b);
外部函数
extern int fun(int a, int b);