一、概念
1 构造数据类型
实际上我们一般不说函数是数据类型,但是函数本身是一种在栈上分配内存的构造数据类型,相比于结构体这样的典型的数据类型,函数的特殊之处在于,它是被调用而不是像结构体、类一样用作实例化对象。
2 函数到底是个什么?
编程语言中的函数概念要比数学中的函数概念更加宽泛,但是二者本质并无差别,都是由三要素构成:输入、处理、输出,比如y = x + 1这是一个函数,x = 1是输入,x + 1是处理方法,y = 2是输出。
3 编程语言中函数的概念
把上面的输入换成参数,处理换成函数体,输出换成返回值,就是编程语言中的函数了。
二、参数、函数体、返回值
1 参数:注意其本质也是局部变量啊!
2 函数体:函数体里写的是算法与数据结构。
3 返回值:只能返回一个值。
三、函数与指针的结合(***重要问题***)
1 参数与指针的结合:传出参数——在实际编程中应用极其广泛
(1)参数分为两种:
指针 == 可传入可传出 == 传址
非指针 == 只可传入不可传出 == 传值
(2)传出是什么意思?
将一个在函数外定义的公共数据的地址传给函数的某个指针类型参数,函数通过操作此指针便可操作这个函数外定义的公共数据,函数执行完毕,公共数据还是公共数据。
2 函数指针——著名的回调函数就是使用函数指针实现的
(1)函数名本身就是一个指针,而且和数组名一样也是一个指向自己的特殊指针:
#include <stdio.h>
int add (int x, int y) {
int z;
return z = x + y;
}
int cheng (int x, int y) {
int z;
z = x * y;
return z;
}
int main() {
// 下面证明了:函数名和数组名一样也是一个指向自己的特殊指针
printf("%p\n", add);
printf("%p\n", &add);
// 下面的输出结果竟然为1?!
printf("%ld\n", sizeof(add));
printf("%ld\n", sizeof(cheng));
return 0;
}
这里有一个问题?(目前无法解释,想必是系统规定的)
既然函数名和数组名一样也是一个指向自己的特殊指针,那么为什么sizeof(函数名)的结果不和sizeof(数组名)一样也是8而是1呢?
(2)如何定义一个函数指针:
#include <stdio.h>
int add (int x, int y) {
int z;
return z = x + y;
}
int main() {
// 函数指针定义和初始化如下所示:
int (*padd) (int x, int y) = add;
// 使用函数指针:
int z = padd(1, 2);
printf("%d\n", z);
return 0;
}
(3)回调函数:传递给某个函数的函数指针类型参数的函数,某个函数我们一般叫注册函数。
#include <stdio.h>
// 回调函数add
int add (int x, int y) {
int z;
printf("回调函数add被调用了!\n");
return z = x + y;
}
// 回调函数minus
int minus (int x, int y) {
int z;
printf("回调函数minus被调用了!\n");
return z = x - y;
}
// 注册函数
int regis (int x, int y, int (*accept) (int x, int y)) {
// 在注册函数中调用回调函数
int z = accept(x, y);
return z;
}
int main() {
int x, y, z;
printf("请输入您要计算的数字:");
int ret_scanf = scanf("%d %d", &x, &y);
// 调用注册函数
if (x <= y) {
z = regis(x, y, add);
printf("%d\n", z);
} else {
z = regis(x, y, minus);
printf("%d\n", z);
}
return 0;
}
回调函数在Linux服务器开发中意义极其重要,但是上面这段代码并不能很好的体现出回调函数的作用:
①提高程序的泛化性:
在上面这段代码中我们使用了一个regis函数负责接收各个回调函数,这里体现了回调函数的一个作用,即泛化,当然我们也可以直接调用回调函数本身而不是通过注册函数调用回调函数,但是这样并不泛化,泛化性是评判程序好坏的一个标准,良好的泛化性使得程序更独立,更易懂。
②在上面这段代码中并不能很好的体现回调函数的”回头再调用“,我们只是将回调函数传进了注册函数,然后在注册函数中调用了一下而已。在实际开发中,我们完全可以这么做:比如我们希望程序在接收到某个信号的时候要处理一些事情,但是我们并不知道信号什么时候到达程序,这是随机的,这时我们就可以定义一个信号捕获函数signal_catch作为注册函数,将我们希望接收到某个信号时做的处理写成一个信号处理函数signal_handle作为回调函数传给信号捕获函数signal_catch,并给signal_catch开一个线程,让这个函数去等信号,一旦该信号过来,回调函数将被调用,就会去处理我们希望程序在接收到该信号时做的事情。以上介绍的便是在进程通信中应用广泛的信号机制,这个机制充分说明了回调函数在C编程中的重大意义。
3 返回值与指针的结合:指针函数——在实际编程中应用极其广泛
其本质和传出参数完全一样,传出参数相当于是解决了C语言函数只能返回一个值的局限;
需注意在返回指针时不能返回一个函数内部定义的局部数据的地址,如果这样做,将会产生野指针!