本文主要是介绍函数指针,才是一切一切的根基,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1. 前言
最近工作中,在干一件事情,就是打通从安卓的java代码,到JNI,最后到server端的C代码。
当然这个过程,早已有大神们做好了,我只是根据当前的业务需要,定制化的实现这个过程。
那这和函数指针,有啥关系呢?请先看程序和内存的关联关系。
2. C语言中的内存布局
C语言的内存布局[1], 如下图所示:
任何一个C语言可执行程序,以有组织的方式加载到计算机内存当中,这段内存被称为进程地址空间或者C程序的内存布局。
通常,将这个内存布局,分为如下几个部分:
(1)Text segment ——文本段
文本段包含C程序的可执行指令,也称为代码段。文本段是不同的进程之间可共享的。
(2)Data segment —— 数据段
这里分为2个子段:初始化数据段和未初始化数据段。通俗的说,不为0的全局变量和静态变量,会存储在初始化数据段。其余的变量会存储在未初始化数据段。
(3)Heap segment —— 堆
在堆区,我们可以动态的开辟和释放内存。开辟内存可以使用malloc(), calloc(), realloc() and new for C++。释放内存可以使用free() or delete。
(4)Stack segment —— 栈
所有函数当中声明的变量,会存储在栈区,存储内容也包括地址,形参等信息。当我们调用某个函数,会创建一个stack frame,当函数返回之后,stack frame将会被销毁。stack pointer将会记录所有的出栈,入栈信息。
每一个函数都拥有自己的stack frame,当调用函数时,stack frame的结构图如下:
(5)Unmapped or reserved —— 预留段
未映射或预留段包含命令行参数和其他程序相关数据,如可执行映像的较低地址,较高地址等。
3. 函数指针
(1)函数指针:指向函数地址的指针。
(A function pointer is a pointer that refers to the address of a function.)
一个典型的函数指针,如下:
int (*func)(int a, float b);
(2)函数指针的例子[2]——异步调用
demo1 :
/ * This code catches the alarm signal generated from the kernelAsynchronously */ #include <stdio.h> #include <signal.h> #include <unistd.h>struct sigaction act;/* signal handler definition goes here */ void sig_handler(int signo, siginfo_t *si, void *ucontext) {printf("Got alarm signal %d\n",signo);/* do the required stuff here */ }int main(void) {act.sa_sigaction = sig_handler;act.sa_flags = SA_SIGINFO;/* register signal handler */sigaction(SIGALRM, &act, NULL); /* set the alarm for 10 sec */ alarm(10); /* wait for any signal from kernel */ pause(); /* after signal handler execution */ printf("back to main\n"); return 0; }
demo2:
/* insertion_sort.h */typedef int (*callback)(int, int); void insertion_sort(int *array, int n, callback comparison);
/* insertion_main.c */#include<stdio.h> #include<stdlib.h> #include"insertion_sort.h"int ascending(int a, int b) {return a > b; }int descending(int a, int b) {return a < b; }int even_first(int a, int b) {/* code goes here */ }int odd_first(int a, int b) {/* code goes here */ }int main(void) {int i;int choice;int array[10] = {22,66,55,11,99,33,44,77,88,0};printf("ascending 1: descending 2: even_first 3: odd_first 4: quit 5\n");printf("enter your choice = ");scanf("%d",&choice);switch(choice){case 1:insertion_sort(array,10, ascending);break;case 2:insertion_sort(array,10, descending);case 3:insertion_sort(array,10, even_first);break;case 4:insertion_sort(array,10, odd_first);break;case 5:exit(0);default:printf("no such option\n");}printf("after insertion_sort\n");for(i=0;i<10;i++)printf("%d\t", array[i]);printf("\n");return 0; }
/* insertion_sort.c */#include"insertion_sort.h"void insertion_sort(int *array, int n, callback comparison) {int i, j, key;for(j=1; j<=n-1;j++){key=array[j];i=j-1;while(i >=0 && comparison(array[i], key)){array[i+1]=array[i];i=i-1;}array[i+1]=key;} }
4. 再探内存地址和程序的关系从以上2个demo,已经可以很明显的看出函数指针的用途和用法。
不过,这和开始说的,函数指针是一切的根基,好像毫无关联啊,怎么看都是一种编程技巧嘛。
别急,往下看。
有人曾经说过,c语言的精华是指针,我认为这句话没有说完,c语言的精华是函数指针才对。
为什么这么说?
既然,程序只不过是内存的一部分。而函数是程序的一部分。
那换句话说,其实程序就是计算机按照一定顺序访问内存地址的过程。
那么,只要能够控制(获取)内存地址,就等于掌握了计算机。
说到这里,函数指针的意义已经很明确了。
那么,安卓的java代码,与JNI的C代码,再到server的C代码,到底怎么交互呢?
很简单,互相传递函数指针,一切搞定!
参考文献:
[1]http://www.firmcodes.com/memory-layout-c-program-2/
[2]http://opensourceforu.com/2012/02/function-pointers-and-callbacks-in-c-an-odyssey/
这篇关于函数指针,才是一切一切的根基的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!