Chinaunix首页 | 论坛 | 博客
  • 博客访问: 143125
  • 博文数量: 29
  • 博客积分: 151
  • 博客等级: 入伍新兵
  • 技术积分: 225
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-20 18:54
文章分类

全部博文(29)

文章存档

2012年(16)

2011年(13)

分类:

2012-02-27 15:05:04

原文地址:函数声明 作者:czysocket_dara

看了C陷阱与缺陷,第一个给我震撼的就是理解函数声明了,下面是我的理解。

1.理解函数声明
    为了模拟开机启动时的情形,我们必须设计出一个C语句,以显示调用位于地址0的子例程。调用语句如下:
    (*(void (*)())0)();
胆颤了吧?首先我们从函数的声明说起:有如下一个函数
    void func(){...}
那么,要想声明一个函数指针,指向这类函数,怎样声明呢?如下:
    void (*pf)();
那么,想要将一个值转换成一个指针,指向这类函数,怎么强制转换呢?如下:
    (void (*)())value
现在value被转换成了一个指向函数的指针,怎么调用它呢?如下:
    ((void) (*)()value)();
或    (*(void) (*)()value)();
或    (******(void) (*)value)();
神奇吧,看了上面的函数调用方式!其实你完全可以这样调用函数:
    func();
    (*func)();
    (********func)();
没有任何问题!

    对于初学的时候,最容易头晕的就是指针符*到底是什么东西?它是变量的一部分?还是声明类型的一部分?其实,仔细回忆C中标识符的定义规则就知道,指针符*必须是类型声明的一部分,因为变量的声明不能含有指针符号,否则是一个非法的变量!
    
2.C语言中的声明
对比简单类型的强制转换我们就可以更加明白上面的强制转换,简单类型定义如下:
    int a;
    char b;
    b=(char)a;
    int *a;
    char *b;
    b=(int *)a;
    其实,在各种指针之间转换很少见,最常见的就是将void指针转换成各种指针,用过malloc族函数吗?
    a=(int *)malloc(sizeof(int));
由于声明符和表达式类似,所以你可以这样声明
    int ((a));    //将a声明为一个int型值
    int *func(),(*fp)();    
    前者是一个函数,后者是一个指针,所以千万不要对指针定义成函数了,分不清概念的时候最容易这样
    int (*fp)(){
        /*do something*/
    }
显然,这里将一个指针定义成了一个函数!这是任何编译器都不能容忍的。
    结合简单变量的类型,出去声明中的变量,得到的就是这个变量的类型,如float a,那么要将一个变量转换成a的类型,那么只需要
(float)value;即可,同理:
float *b,除去变量b,得到b的类型是(float *)。
float (*fp)();除去变量fp,得到的类型是float (*)(),所以要将变量转换成fp类型的值时,只需要(float (*)())value,即可!这样value即被转换成一个函数指针了,也就像最前面的例子中那样!
如果,更复杂,有声明如下:
float * (*fp)();    //返回一个float指针
其实,这也是唬人的,同理可知:强制转换类型为(float * (*)())value;

3.其他考量
    看到第一个例子时,可能想,能不能这样子:(*0)();呢?
不行,因为0是一个数字,而*必须要操作一个指针,而且对于要调用的函数是void型的,所以这个指针应该转换成相应的类型,所以需要将0转成一个指向void返回值的参数为空的函数的指针。
    最后,linux内核中的信号处理函数定义如下:
void (*signal(int,void (*)(int)))(int);
首先,将上面的函数声明看成这样void (*p)(int);可知,p是一个函数,所以signal函数的返回类型为一个函数指针,指向的函数类型是
void (*)(int);使用typedef可以简化上面signal函数的声明
typedef void (*HANDLER)(int);
HANDLER signal(int,HANDLER);
解释一下:signal是一个返回函数指针的函数,返回的指针指向的函数类型是void (*)(int);而signal的参数一个是int,另一个是一个函数,类型为void (*)(int),刚好就是最初声明的样子。




[转]理解C语言函数声明(摘自C陷阱和缺陷)



摘自C陷阱和缺陷一书,但被网上一个自认为是高手的挫人改成这个样子,凑合着看吧

声明与函数 有一段程序存储在起始地址为0的一段内存上,如果我们想要调用这段程序,请问该如何去做?   

答案 答案是(*(void (*)( ) )0)( )。看起来确实令人头大,那好,让我们知难而上,从两个不同的途径来详细分析这个问题。   

答案分析:从尾到头   

首先,最基本的函数声明:void function (paramList); 最基本的函数调用:function(paramList); 鉴于问题中的函数没有参数,函数调用可简化为 function();   

其次,根据问题描述,可以知道0是这个函数的入口地址,也就是说,0是一个函数的指针。使用函数指针的函数声明形式是:void (*pFunction)(),相应的调用形式是: (*pFunction)(),则问题中的函数调用可以写作:(*0)( )。  

第三,大家知道,函数指针变量不能是一个常数,因此上式中的0必须要被转化为函数指针。我们先来研究一下,对于使用函数指针的函数:比如void (*pFunction)( ),函数指针变量的原型是什么? 这个问题很简单,pFunction函数指针原型是( void (*)( ) ),即去掉变量名,清晰起见,整个加上()号。所以将0强制转换为一个返回值为void,参数为空的函数指针如下:( void (*)( ) )。 OK,结合2)和3)的分析,结果出来了,那就是:(*(void (*)( ) )0)( ) 。   

答案分析:从头到尾理解答案   

(void (*)( )) ,是一个返回值为void,参数为空的函数指针原型.(void (*)( ))0,把0转变成一个返回值为void,参数为空的函数指针,指针指向的地址为0.   *(void (*)( ))0,前面加上*表示整个是一个返回值为void的函数的名字  (*(void (*)( ))0)( ),这当然就是一个函数了。我们可以使用typedef清晰声明如下:   typedef void (*pFun)( ); 这样函数变为 (*(pFun)0 )( );

问题:三个声明的分析  对声明进行分析,最根本的方法还是类比替换法,从那些最基本的声明上进行类比,简化,从而进行理解,下面通过分析三个例子,来具体阐述如何使用这种方法。

#1:int* (*a[5])(int, char*);   

首先看到标识符名a,"[]"优先级大于"*",a与"[5]"先结合。所以a是一个数组,这个数组有5个元素,每一个元素都是一个指针,指针指向" (int, char*)",很明显,指向的是一个函数,这个函数参数是"int, char*",返回值是"int*"。OK,结束了一个。:)

#2:void (*b[10]) (void (*)());   

 b是一个数组,这个数组有10个元素,每一个元素都是一个指针,指针指向一个函数,函数参数是"void (*)()"【注10】,返回值是"void"。完毕!   注意:这个参数又是一个指针,指向一个函数,函数参数为空,返回值是"void"。

#3. double(*)() (*pa)[9];    pa是一个指针,指针指向一个数组,这个数组有9个元素,每一个元素都是"double(*)()"(也即一个函数指针,指向一个函数,这个函数的参数为空,返回值是"double")。

(自己注:#3看起来有点难理解,如果把double(*)()换成int, 变成int (*pa)[9]这样就不难理解了)

阅读(1010) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~