此生既入苦寒山,何妨再攀险峰!
分类:
2011-08-24 11:46:26
原文地址:关于C语言的函数指针 作者:myleeming
今年由于老美造成的金融危机,工作很不好找,嵌入式还相对好一点,毕竟要的人多,今天听正在找工作的师兄说:那些中心3、4楼学模拟的都在狂学C了。可见c语言关键时刻还是吃饭的家伙啊!所以更应该好好学了!
trio老师的名言:想学好C就得学好指针,想学好指针就得学好函数指针,呵呵,今天就着trio老师的讲义和网上的一些资料,把函数指针的一些概念,思想好好整理一下,如下:
●函数指针的声明:
函数指针即指向函数地址的指针。利用该指针可以知道函数在内存中的位置。因此也可
以利用函数指针调用函数。函数指针的声明方法是:
<类型> ( *函数指针变量名 )( 函数的参数列表 );
例:int (*pf)(int);
让我们来分析一下,左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(int)和由右边圆括弧中的入口参数(本例中参数int型)。注意本例中还没有创建指针变量-只是声明了变量类型。目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小。
●如何操作一个函数指针:
简单声明一个函数指针并不意味着它马上就可以使用。和其它指针一样,对函数指针执行间接访问之前必须把它初始化为指向某个函数
int f(int);
int (*pf)(int)=&f;
第 2 个声明创建了函数指针 pf ,并把它初始化为指向函数 f 。 函数指针的初始化也可以通过一条赋值语句来完成。 在函数指针的初始化之前具有 f 的原型是很重要的,否则编译器就无法检查 f 的类型是否与 pf 所指向的类型一致。
初始化表达式中的 & 操作符是可选的,因为函数名被使用时总是由编译器把它转换为函数指针。 & 操作符只是显式地说明了编译器隐式执行的任务。
●三种调用方法
ans=f(25);
ans=(*pf)(25);
ans=pf(25);
第 1 条语句简单地使用名字调用函数 f ,但它的执行过程可能和你想象的不太一样。 函数名 f 首先被转换为一个函数指针,该指针指
定函数在内存中的位置。然后, 函数调用操作符调用该函数,执行开始于这个地址的代码。
第 2 条语句对 pf 执行间接访问操作,它把函数指针转换为一个函数名。这个转换并不是真正需要的,因为编译器在执行函数调用操作符
之前又会把它转换回去。不过,这条语句的效果和第1条是完全一样的。
第 3 条语句和前两条的效果是一样的。间接访问并非必需,因为编译器需要的是一个函数指针。
●用途
你不会每天都使用函数指针,但是,它们确有用武之地,特别是多态、回调和多线程的实现。
我个人觉得多态的使用在linux开发下非常有用和常见。这里详细说一下多态
以下复制trio老师高级C语言编程部分内容:
利用函数指针实现多态是很多系统软件常用的方法, 比如在操作系统中为了能够支持不
同硬件设备的统一管理,往往会定义一个内部的数据结构,这个结构中定义了具体的硬件操
作函数的函数指针,当然针对不同的硬件设备,这些函数指针指向不同的操作函数。当上层
软件需要访问某个设备时,操作系统将根据这个数据结构调用不同的操作函数,这就使得虽
然底层的操作函数各不相同,但是上层的软件却可以统一。下面是
struct file_operations {struct module *owner;
loff_t (*llseek)(struct file *, loff_t, int);
ssize_t (*read)(struct file *, char *, size_t, loff_t *);
ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
int (*readdir)(struct file *, void *, filldir_t);
unsigned int (*poll)(struct file *, struct poll_table_struct *);
int (*ioctl)(struct inode *, struct file *, unsigned int, unsigned long);
int (*mmap)(struct file *, struct vm_area_struct *);
int (*open)(struct inode *, struct file *);
int (*flush)(struct file *);
int (*release)(struct inode *, struct file *);
int (*fsync)(struct file *, struct dentry *, int datasync);
int (*fasync)(int, struct file *, int);
int (*lock)(struct file *, int, struct file_lock *);
ssize_t (*readv)(struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*writev)(struct file *, const struct iovec *, unsigned long, loff_t *);
ssize_t (*sendpage)(struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long,
unsigned long, unsigned long);
#ifdef MAGIC_ROM_PTR
int (*romptr)(struct file *, struct vm_area_struct *);
#endif /* MAGIC_ROM_PTR */
};
这个结构的每一个成员的名字都对应着一个系统调用。 当用户进程利用系统调用对设备
文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程
序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这就是linux的设
备驱动程序工作的基本原理。因此,编写设备驱动程序的主要工作就是编写这些具体的子函
数,并赋给file_operations的各个域。
《高级C语言编程》——“可以说多态是函数指针最重要的一个应用之一, 我个人认为这也是程序员从初级编程进入高级编程阶段的一个重要里程碑。”
努力中!