`

声明与函数、函数指针---(*(void (*)( ) )0)( ) 解析

    博客分类:
  • c++
阅读更多

 

概述 

 

   在很多情况下,尤其是读别人所写代码的时候,对 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. doube(*)() (*pa)[9]; 

 

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

 

C语言中的函数指针

 

函数在内存中有一个物理位置,而这个位置是可以赋给一个指针的。一零点函数的地址就是该函数的入口点。因此,函数指针可被用来调用一个函数。函数的地址是用不带任何括号或参数的函数名来得到的。(这很类似于数组地址的得到方法,即,在只有数组名而无下标是就得到数组地址。)

 

怎样说明一个函数指针变量呢 ? 

为了说明一个变量 fn_pointer 的类型是"返回值为 int 的函数指针", 你可以使用下面的说明语句:

int (*fn_pointer) ();

为了让编译器能正确地解释这句语句, *fn_pointer 必须用括号围起来。若漏了这对括号, 则:

int *fn_pointer ();

的意思完全不同了。fn_pointer 将是一个函数名, 其返回值为 int 类型的指针。

 

函数指针变量   

 

  在C语言中规定,一个函数总是占用一段连续的内存区,   而函数名就是该函数所占内存区的首地址。   我们可以把函数的这个首地址 ( 或称入口地址 ) 赋予一个指针变量,   使该指针变量指向该函数。然后通过指针变量就可以找到并调用这个函数。   我们把这种指向函数的指针变量称为 " 函数指针变量 " 。   

函数指针变量定义的一般形式为:   

类型说明符  (* 指针变量名 )();   

其中 " 类型说明符 " 表示被指函数的返回值的类型。 "(*  指针变量名 )" 表示 "*" 后面的变量是定义的指针变量。   最后的空括号表示指针变量所指的是一个函数。   

例如:  int (*pf)();  

表示 pf 是一个指向函数入口的指针变量,该函数的返回值 ( 函数值 ) 是整型。   

下面通过例子来说明用指针形式实现对函数调用的方法。   

int max(int a,int b){  

if(a>b)return a;  

else return b;  

}  

main(){  

int max(int a,int b);  

int(*pmax)();  

int x,y,z;  

pmax=max;  

printf("input two numbers:/n");  

scanf("%d%d",&x,&y);  

z=(*pmax)(x,y);  

printf("maxmum=%d",z);  

}  

  从上述程序可以看出用,函数指针变量形式调用函数的步骤如下:

 

1.  先定义函数指针变量,如后一程序中第 9 行  int (*pmax)(); 定义 pmax 为函数指针变量。   

 

2.  把被调函数的入口地址 ( 函数名 ) 赋予该函数指针变量,如程序中第 11 行  pmax=max;  

 

3.  用函数指针变量形式调用函数,如程序第 14 行  z=(*pmax)(x,y);  调用函数的一般形式为:  (* 指针变量名 ) ( 实参表 ) 使用函数指针变量还应注意以下两点:   

 

a.  函数指针变量不能进行算术运算,这是与数组指针变量不同的。数组指针变量加减一个整数可使指针移动指向后面或前面的数组元素,而函数指针的移动是毫无意义的。   

 

b.  函数调用中 "(* 指针变量名 )" 的两边的括号不可少,其中的 * 不应该理解为求值运算,在此处它只是一种表示符号。   

 

指针型函数   

 

前面我们介绍过,所谓函数类型是指函数返回值的类型。   在C语言中允许一个函数的返回值是一个指针 ( 即地址 ) ,   这种返回指针值的函数称为指针型函数。   

定义指针型函数的一般形式为:    

类型说明符  * 函数名 ( 形参表 )   

{   

...... /* 函数体 */  

}   

其中函数名之前加了 "*" 号表明这是一个指针型函数,即返回值是一个指针。类型说明符表示了返回的指针值所指向的数据类型。   

如:   

int *ap(int x,int y)  

{  

...... /* 函数体 */  

}  

  表示 ap 是一个返回指针值的指针型函数,   它返回的指针指向一个整型变量。下例中定义了一个指针型函数  day_name ,它的返回值指向一个字符串。该函数中定义了一个静态指针数组 name 。 name  数组初始化赋值为八个字符串,分别表示各个星期名及出错提示。形参 n 表示与星期名所对应的整数。在主函数中,   把输入的整数 i 作为实参,   在 printf 语句中调用 day_name 函数并把 i 值传送给形参  n 。 day_name 函数中的 return 语句包含一个条件表达式,  n  值若大于 7 或小于 1 则把 name[0]  指针返回主函数输出出错提示字符串 "Illegal day" 。否则返回主函数输出对应的星期名。主函数中的第 7 行是个条件语句,其语义是,如输入为负数 (i<0) 则中止程序运行退出程序。 exit 是一个库函数, exit(1) 表示发生错误后退出程序,  exit(0) 表示正常退出。   

 

  应该特别注意的是函数指针变量和指针型函数这两者在写法和意义上的区别。如 int(*p)() 和 int *p() 是两个完全不同的量。 int(*p)() 是一个变量说明,说明 p  是一个指向函数入口的指针变量,该函数的返回值是整型量, (*p) 的两边的括号不能少。 int *p()  则不是变量说明而是函数说明,说明 p 是一个指针型函数,其返回值是一个指向整型量的指针, *p 两边没有括号。作为函数说明,   在括号内最好写入形式参数,这样便于与变量说明区别。   对于指针型函数定义, int *p() 只是函数头部分,一般还应该有函数体部分。   

main(){  

int i;  

char *day_name(int n);   

printf("input Day No:/n");  

scanf("%d",&i);  

if(i<0) exit(1);  

printf("Day No:%2d-->%s/n",i,day_name(i));  

}  

char *day_n

 

ame(int n){  

static char *name[]={ "Illegal day",  

"Monday",  

"Tuesday",  

"Wednesday",  

"Thursday",  

"Friday",  

"Saturday",  

"Sunday"};  

return((n<1||n>7) ? name[0] : name[n]);  

}  

  本程序是通过指针函数,输入一个 1 ~ 7 之间的整数,   输出对应的星期名。指针数组的说明与使用一个数组的元素值为指针则是指针数组。   指针数组是一组有序的指针的集合。   指针数组的所有元素都必须是具有相同存储类型和指向相同数据类型的指针变量。   

  指针数组说明的一般形式为:   类型说明符 * 数组名 [ 数组长度 ]   

  其中类型说明符为指针值所指向的变量的类型。例如:  int *pa[3]  表示 pa 是一个指针数组,它有三个数组元素,   每个元素值都是一个指针,指向整型变量。通常可用一个指针数组来指向一个二维数组。   指针数组中的每个元素被赋予二维数组每一行的首地址,   因此也可理解为指向一个一维数组。图 6—6 表示了这种关系。   

int a[3][3]={1,2,3,4,5,6,7,8,9};  

int *pa[3]={a[0],a[1],a[2]};  

int *p=a[0];  

main(){  

int i;  

for(i=0;i<3;i++)  

printf("%d,%d,%d/n",a[i][2-i],*a[i],*(*(a+i)+i));  

for(i=0;i<3;i++)  

printf("%d,%d,%d/n",*pa[i],p[i],*(p+i));  

}  

  本例程序中, pa 是一个指针数组,三个元素分别指向二维数组 a 的各行。然后用循环语句输出指定的数组元素。其中 *a[i] 表示 i 行 0 列元素值; *(*(a+i)+i) 表示 i 行 i 列的元素值; *pa[i] 表示 i 行 0 列元素值;由于 p 与 a[0] 相同,故 p[i] 表示 0 行 i 列的值; *(p+i) 表示 0 行 i 列的值。读者可仔细领会元素值的各种不同的表示方法。   应该注意指针数组和二维数组指针变量的区别。   这两者虽然都可用来表示二维数组,但是其表示方法和意义是不同的

 

转自:http://blog.csdn.net/norbe/article/details/624257

分享到:
评论

相关推荐

    基于STM32F103系列单片机的FreeModbus从机程序源码.zip

    基于STM32F103系列单片机的FreeModbus从机程序源码,可供学习.../*------------------------------------输入寄存器处理函数------------------------------------------ * UCHAR * pucRegBuffer :返回数据指针 * U

    彻底搞定C指针-函数名与函数指针

    函数名与函数指针 一 通常的函数调用 一个通常的函数调用的例子: //自行包含头文件 void MyFun(int x); //此处的申明也可写成:void MyFun( int ); int main(int argc, char* argv[]) { MyFun(10); //这里是调用...

    动态数组实现(所有代码均使用C语言回调函数实现及存储数据均使用void*、void**实现)

    rar文件包含:Vector.h、Vector.c、...存储数据使用void*、void**,其中包括结构体数据结构。主要功能有初始化动态数组、释放动态数组、尾插法、删除指定下标、更新指定下标数据、打印数据、获取数据对应的指定下标等。

    函数指针.txt

    让我们来分析一下 左边圆括弧中的星号是函数指针声明的关键 另外两个元素是函数的返回类型(void)和右边圆括弧中的入口参数(本例中参数是空) 注意本例中还没有创建指针变量 只是声明了变量类型 "&gt;函数指针 void...

    hook api lib.zip

    然后hookproc的函数声明如下: HMODULE WINAPI My_LoadLibraryA(DWORD RetAddr, __pfnLoadLibraryA pfnLoadLibraryA, LPCTSTR lpFileName ); 比原来的多了2个参数,参数位置不能颠倒,在My...

    C++函数指针和回调函数使用解析

    函数指针变量的声明: typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针变量 实例 以下实例声明了函数指针变量 p,指向函数 max: #include int max(int x, int y){ return x &gt; y ? ...

    LCD12864显示汉字和图案(C语言)

    0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x04,0x0F,0xF8,0x10,0x07,0xFF,0xFF,0xFF,0xFA,0x00, 0x00,0x07,0xFF,0xFF,0xF0,0x00,0x04,0x07,0xF0,0x10,0x07,0xFF,0xFF,0xFF,0xFA,0x00, 0x00,0xFF,0xFF,0xFF,0xF8,0x00,0x02,0x...

    双链表实现(所有代码均使用C语言回调函数实现及存储数据均使用void*、void**实现)

    rar文件包含:DoubleList.h、DoubleList.c、main.c...存储数据使用void*、void**,其中包括结构体数据结构。主要功能有初始化双链表、释放双链表、尾插法、任意插入法、任意删除法、打印数据(从左至右、从右至左)等。

    第8章 指针-8函数指针的典型应用-21

    第8章 指针——函数指针的典型应用:通用排序C语言程序设计第8章 指针如果不使用函数指针编程…编程实现升序和降序排序//选择法升序排序void Ascendin

    跨平台解析XML文件 XmlNode V1.01测试版

    Return: 创建完成的兄弟节点指针,当返回0时,表示创建兄弟节点失败 Mark: 当前节点的父节点类型不是XML_OBJECT_TYPE_ELEMENT或XML_OBJECT_TYPE_DOCUMENT,不能创建子节点 输入的节点类型可以是XML_OBJECT_TYPE_...

    C++智能指针-unique-ptr智能指针详解.pdf

    C++智能指针 智能指针_unique_ptr智能指针详解 智能指针详解 作为智能指针的⼀种,unique_ptr 指针⾃然也具备"在适当时机⾃动释放堆内存空间"的能⼒。和 shared_ptr 指针最⼤的不同之处在 于,unique_ptr 指针指向的...

    文件系统代码

    /*-----------函数事先申明--------------------*/ int format(); int mkdir(char *sonfname); int rmdir(char *sonfname); int create(char *name); int listshow(); int delfile&#40;char *name&#41;; int change...

    C/C++面试之算法系列--几个典型的内存拷贝及字符串函数实现

    void* memcpy( void *dest, const void *src, size_t count ) { char* pdest = static_cast*&gt;( dest ); const char* psrc = static_cast*&gt;( src ); // 依次从前拷贝,目的地址覆盖了源地址的数,此时从后往前...

    题目:显示宠物信息(Dev C++运行可通过但是会警告)(纯虚函数、父类指针、动态多态性、继承)

    纯虚函数void Speak (); 纯虚函数void GetInfo; (2) 在Pet类的基础上派生出猫类Cat: 数据成员(protected): int id; // 猫的编号; 函数成员(public): 构造函数 (提示: 自已定义其形式,注意基类的构造...

    C++对象和指针的引用

    //用对象指针调用指向类成员函数指针pfun指向的函数 } 以上程序定义了好几个指针,虽然它们都是指针,但是所指向的对象是不同的。p是指向类的对象;pc是指向类的数据成员;pfun是指向类的成员函数。因此它们的值...

    成员函数用于回调函数

    如qsort 等函数需要函数指针才能回调 用此函数库可以将成员函数指针转为普通函数指针 测试代码如下 #include #include #include #include #include #include using cmpfunc = int(__cdecl*)(const void*, ...

    第8章 指针-2指针变量作函数参数 - 典型实例 - 错误案例分析1

    第8章 指针——指针变量做函数参数:错误案例分析C语言程序设计第8章 指针计算最高分及其学号void FindMax(int score[],long num

    指针数组和数组指针的区别.doc

    //函数原形声明 void main(void) { //-----------------------------段1----------------------------------------- char *a[]={"abc","cde","fgh"};//字符指针数组 char* *b=a;//定义一个指向指针的指针,并...

    小型售货机

    virtual void Set_Sellp(float price)=0; //设置货物卖价,纯虚函数 int* Get_Goodnum(); //得到各个数据成员的地址,方便储存在文件中 int* Get_Innum(); float* Get_Buyp(); float* Get_Sellp(); }; ...

    C++智能指针:auto-ptr详解.pdf

    } } 运⾏结果: 我们可以看到在绑定时输出Simple:1,之后也能正常实现Test类中的功能,同时my_auto可以通过get⽅法进⾏裸指针赋值以及使⽤*进⾏ 解引⽤操作,与普通指针⽆异。最后函数结束后,在调⽤析构函数的...

Global site tag (gtag.js) - Google Analytics