接续上篇[C语言基础及指针③函数与二级指针](http://www.jianshu.com/p/184df8a1f195)
在上一篇中 , 我们学习了函数与二级指针 , 函数和java中的方法类似 , 只是缺少了访问控制符 , 二级指针也就是指针的指针 , 指针里面存储的是指针的地址 , 可以通过`*`操作符不断往上追溯 , 然后通过内存地址操作内存空间 。
### 函数指针
当我们定义一个函数的时候 , 这个函数也会像变量一样 , 会有一个内存地址 , 我们也可以将函数定义成为一个函数指针 , 但函数不同于变量 , 变量存储的是固定的值 , 而函数指针存储的是函数的内存地址 。下面我就用一个示例来说明:
```c
// windows 弹出框头文件
#include <Windows.h>
/*函数*/
void message() {
MessageBox(NULL, "我是弹出框", "消息", NULL);
}
void main() {
// 函数指针定义 , 返回值(函数指针名称)(函数参数) = 函数名称
void(*func_p)() = &message;
// 调用函数指针
func_p();
printf("函数指针地址:%#x\n", func_p);
getchar();
}
```
输出:
```c
函数指针地址:0xe6d31073
```
我们可以通过函数指针地址 , 反汇编我们的程序员 , 查看他的跳转
![函数指针汇编1.png](http://upload-images.jianshu.io/upload_images/643851-54683d30e1dcf48b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
由上图我们可以看出 , 我们的函数指针 , 通过jmp指令跳转到另一个地址上 , 下面我来看看message的地址里面是什么:
![函数指针汇编2.png](http://upload-images.jianshu.io/upload_images/643851-1653c46da9f17dc7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我们看到通过函数指针里面存储的是一个函数的地址 ,然后通过一个jmp指令调到我们的函数定义执行 。
有了我们的函数指针 , 我们可以做很多事情 , 下面我们来看一个简单的示例:
```c
int add(int num1, int num2) {
return num1 + num2;
}
int minus(int num1, int num2) {
return num1 - num2;
}
// 将函数指针直接定义到函数形参中 , 类似java中的多态
// 我们可以函数指针作为函数参数传入
void showMsg(int(*c)(int num1, int num2), int a, int b) {
int r = c(a, b);
printf("计算完成=%d\n", r);
}
void main() {
showMsg(add, 10, 10);
showMsg(minus, 30, 2);
}
getchar();
```
输出:
```c
计算完成=20
计算完成=28
```
通过传入函数地址 , 就可以调用函数进行运算 , 我们只要按照传入的参数和返回值 , 写我们自己的函数 , 通过这个方法 , 我们就可以统一的实现我们函数的功能 。下面我们来写一个回调函数:
```c
/*模拟网络请求回调*/
void requestNet(char* url , void(*callBack)(char *)) {
printf("请求地址:%s , 正在请求网络....\n",url);
// 模拟网络请求耗时
Sleep(2000);
char* str = "我是请求的网络数据 , 落花有意随流水 , 流水无情恋落花\n";
callBack(str);
}
/*回调函数*/
void netCallBack(char *str) {
printf("网络请求回调\n");
printf("请求到的数据:%s" ,str);
}
void main() {
char* url = "www.zhuyongit.com";
requestNet(url, netCallBack);
}
```
输出:
```c
请求地址:www.zhuyongit.com , 正在请求网络....
网络请求回调
请求到的数据:我是请求的网络数据 , 落花有意随流水 , 流水无情恋落花
```
我们模拟了网络请求的常见封装 , 使用一个回调函数来接收我们请求回来的数据 。函数指针很强大 , 我们可以直接传入函数名称 , 再另一个函数里面执行我们传入的函数 , 如果是在java里面 ,我还需要传入一个对象 , 再通过对象来调用方法 , 在C语言里面 , 直接通过函数指针就可以搞定 。
Android程序员学C系列:
[C语言基础及指针①](http://www.jianshu.com/p/4701cd1e1914)
[C语言基础及指针②之指针内存分析](http://www.jianshu.com/p/d556070b12ef)
[C语言基础及指针③函数与二级指针](http://www.jianshu.com/p/184df8a1f195)
[C语言基础及指针④函数指针](http://www.jianshu.com/p/181b50e02c89)
[C语言基础及指针⑤动态内存分配](http://www.jianshu.com/p/93db7c692d1b)
[C语言基础及指针⑥字符操作](http://www.jianshu.com/p/b7e6fc094087)
[C语言基础及指针⑦结构体与指针](http://www.jianshu.com/p/36cc18151e87)
- 简介
- C语言基础及指针①语法基础
- C语言基础及指针②之指针内存分析
- C语言基础及指针③函数与二级指针
- C语言基础及指针④函数指针
- C语言基础及指针⑤动态内存分配
- C语言基础及指针⑥字符操作
- C语言基础及指针⑦结构体与指针
- C语言基础及指针⑧文件IO
- C语言基础及指针⑨联合体与枚举
- C语言基础及指针⑩预编译及jni.h分析
- JNI开发系列①JNI概念及开发流程
- JNI开发系列②.h头文件分析
- JNI开发系列③C语言调用Java字段与方法
- JNI开发系列④C语言调用构造方法
- JNI开发系列⑤对象引用的处理
- NDK开发基础①使用Android Studio编写NDK
- NDK开发基础②文件加密解密与分割合并
- NDK开发基础③增量更新之服务器端生成差分包
- C++基础①命名空间结构体和引用