函数
1. 函数的基础知识
数学中的函数,大家都不陌生,一般的形式是 f(x) = y,x 是自变量,y 是函数值。程序中的函数,和数学中的函数基本一致,有自变量,我们称作“传入参数”,还有函数值,我们叫做返回值。
- 第一个部分,是函数返回值的类型。
- 第二个部分,是函数名字,调用函数的时候,需要给出这个函数名,所以在设计函数名的时候,要尽量设计一个与函数功能有关的名字,例如上图中的函数,通过名字我们可知,这就是一个求平方的函数。
- 第三部分,是传入参数,就是数学函数中的自变量。
- 第四部分就是函数体,也就是要完成函数功能的逻辑代码,结果值是通过 return 语句进行返回的,而整个函数体的逻辑要包裹在一对大括号内部。
#include <stdio.h>
int square(int x) { // 定义函数 square
return x * x;
}
int main() {
int n;
scanf("%d", &n);
printf("%d
", square(n));
return 0;
}
上述代码中,在主函数中,我们读入一个整型数字 n,然后输出 n 的平方值。这里在计算 n 的平方值的时候,程序中调用了上面定义的 square 函数,那么 printf 函数相当于输出的是 square 函数的返回值,根据 square 函数的实现,如果传入的值是 x,那么返回值就是 x * x ,即 x 的平方值。
2. 普通变量的函数传递参数
函数的参数传递过程,就是“实参”给“形参”赋值的过程,“实参”与“形参”之间互相独立,互不影响。
#include <stdio.h>
void add(int n, int m) {
n += m;
return ;
}
int main() {
int n, m;
scanf("%d%d", &n, &m);
add(n, m);
printf("%d
", n);
return 0;
}
函数的参数传递过程,就是“实参”给“形参”赋值的过程。
这个程序中,主函数中的变量 n 就是“实参”,add 函数中的参数 n 就是“形参”,虽然两者名字一样,可完全是两个互相独立的变量。
3. 数组的函数传参
#include <stdio.h>
void add(int *p, int n) {
for (int i = 1; i < n; i++) {
p[0] += p[i];
}
return ;
}
int main() {
int arr[10] = {1, 2, 3};
add(arr, 3);
printf("%d", arr[0]);
return 0;
}
你运行这段程序,会发现输出的结果是 6,意味着数组中的第一个元素的值发生了变化。再想想今天我们要记住的那句话:函数的参数传递过程,就是“实参”给“形参”赋值的过程,“实参”与“形参”之间互相独立,互不影响。
这里面我们的“实参”,实际上是数组的首地址,形参是存储这个首地址的函数参数中的那个指针变量。也就是说,在 add 函数内部,操作的地址空间,和主函数中的那个数组的空间是一个空间,这就是为什么传递数组时,相关元素的值在函数内部可以被改掉的一个原因,因为传递的是地址!
4. 传入与传出参数
#include <stdio.h>
void calc(int x, int *p) {
*p = x * x;
return ;
}
int main() {
int n, m;
scanf("%d", &n);
calc(n, &m);
printf("%d
", m);
return 0;
}
开始先定义了一个 calc 函数,calc 函数有两个参数,第一个是一个整型参数,第二个是一个整型地址,函数内部,将 x 的平方值存储到了 p 所指向的存储空间中。在主函数中调用了 calc 函数,分别传入 n 的值和 m 的地址,然后输出 m 的值,最后你会发现输出的 m 值,就是 n 的平方值。
首先第一个参数 x,是外部传入的一个值,这个值在函数内部,要参与重要的运算过程,也就是说,这个值的感觉更像是从外部传入到内部,然后在函数内部发挥作用,这种类型的参数,我们就叫“传入参数”。
再看 calc 函数的第二个参数,传入的是一个地址。在函数内部的作用,只是将计算得到的一些结果存储在这个地址所指向的空间中,而记录的这些结果,在函数内部是没有用的,是要等到函数执行完后,回到调用者之后,例如上面的主函数内部,才有用。这一个参数的作用,更像是把值从 calc 内部带出到主函数内部而设计的,这类参数,我们叫做“传出参数”。
就像上面代码中看到的,“传入参数”一般就是把值传进去就行,而“传出参数”由于要把值从函数中带出来,一般要传变量地址进去,这样,函数内部才能准确的把结果写入到相关地址所对应的内存中。
思考题(1):数组和函数的思考
请思考如下两个概念的异同:
一个整型数组元素,例如:arr[100]
一个传入整型并且返回整型的函数,例如:func(100)
第一个是申请的占用连续内存空间的数组元素
第二个是申请100个整型数据,内存空间不一定连续
数组只能一次返回一个,函数可以返回多个
思考题(2):如何确定存在知识的盲区
什么叫“存在知识的盲区”呢?就是当你面对一片黑暗的时候,你可以确定这里一定有知识,而不仅仅只是一片黑暗。就像今天我们学习了函数的相关知识,自然的,就会反问自己一句,这些就是函数知识的全部了么?我们如何来确定这个问题的答案呢?很简单,根据已知推未知。
总结
- 函数的作用,是做功能封装,以便在程序其他地方复用相关功能。
- C 语言中的函数的传参过程,是实参给形参赋值的过程,改变形参的值,不会影响实参。
- 在函数参数设计中,一定要分清楚,传入参数和传出参数在功能上的差别。