<<c陷阱与缺陷 >>--“ 理解函数声明 ”
有一次,一个程序员与我交谈一个问题。他当时正在编写一个独立运行于某种微处理器上的c程序。当计算机启动时,硬件将调用首地址为0位置的子例程。为了模拟开机启动时的情形,我们必须设计出个c语句,以显式调用该于例程。经过一段时间的思考,我们最后得到的语句如下:
(*(void(*)())0)();
像这样的表达式恐怕会令每个c程序员的内心都“不寒而栗”。然而,他们大可不必对此望而生畏,因为构造这类表达式其实只有一条简单的规则:按照使用的方式来声明。
任何c变量的声明都由两部分组成:类型以及一组类似表达式的声明符(declarator)。声明符从表面上看与表达式有些类似,对它求值应该返回一个声明中给定类型的结果。最简单的声明符就是单个变量。如:
float f, g;
这个声明的含义是:当对其求值时,表达式f和g的类型为浮点数类型(float)。因为声明符与表达式的相似,所以我们也可以在声明符中任意使用括号:
float ((f));
这个声明的含义是:当对其求值时,((f)的类型为浮点类型,由此可以推知,f也是浮点类型。
同样的逻辑也适用于函数和指针类型的声明,例如:
float ff();
这个声明的含义是:表达式 ff() 求值结果是一个浮点数,也就是说ff是一个返回值为浮点粪型的函数。类似地,
float *pf;
这个声明的含义是*pf是个浮点数,也就是说.pf是一个指向浮点数的指针。
以上这些形式在声明中还可以组合起来,就像在表达式中进行组合一样。因此,
float *g() , (*h)();
表示,*g() 与 (*h)() 是浮点表达式。因为 () 结合优先级高于 *,g()也就是*(g()) :g是一个函数,该函数的返回值类型为指向浮点数的指针。同理,可以得出h是一个函数指针,h所指向函数的返回值为浮点类型。
一旦我们知道了如何声明一个给定类型的变量,那么该类型的类型转换符就很容易得到了:只需要把声明中的变量名和声明末尾的分号去掉,再将剩余的部分用一个括号整个“封装”起来即可。例如,因为下面的声明:
float (*h)();
表示h是一个指向返回值为浮点类型的函数的指针,因此,
(float(*)());
表示一个“指向返回值为浮点类型的函数的指针”的类型转换符。
拥有了这些预备知识,我们现在可以分两步来分析表达式(*(void(*)())0)()。
第一步,假定变量fp是一个函数指针,那么如何调用fp所指向的函数昵?调用方法如下:
(*fp)();
因为fp是一个函数指针,那么 *fp 就是该指针所指向的函数,所以 (*fp)() 就是调用该函数的方式。ANSIC标准允许程序员将上式简写为fp() ,但是一定要记住这种写法只是一种简写形式。
在表达式 (*fp)() 中,*fp两侧的括号非常重要,因为函数运算符 () 的优先级高于单目运算符*。如果 *fp 两侧没有括号,那么*fp() 实际上与 *(fp()) 的含义完全一致,ANSIC把它作为 *((*fp)()) 的简写形式。
现在,剩下的问题就只是找到一个恰当的表达式来替换fp。我们将在分析的第二步来解决这个问题。如果C编译器能够理解我们大脑中对于类型的认识,那么我们可以这样写:
(*0)();
上式并不能生效,因为运算符 * 必须要一个指针来做操作数。而且,这个指针还应该是个函数指针,这样经运算符 * 作用后的结果才能作为函数被调用。因此,在上式中必须对0作类型转换,转换后的类型可以大致描述为:“指向返回值为void类型的函数的指针”。
如果fp是一个指向返回值为void类型的函数的指针,那么(*fp)()的值为void,fp的声明如下:
void (*fp)();
因此,我们可以用下式来完成调用存储位置为0的子例程:
void (*fp)();
(*fP)();
译注:这种写法fp默认初始化为0 ,不提倡。
这种写法的代价是多声明了一个“哑”变量。
但是,我们一旦知道如何声明一个变量,也就自然知道如何对一个常数进行类型转换,将其转型为该变量的类型:只需要在变量声明中将变量名去掉即可。
因此,将常数0转型为“指向返回值为void的函数的指针”类型,可以这样写:
(void(*)())0;
因此,我们可以用(void(*)())0来替换fp,从而得到:
(*(void(*)())0)();
末尾的分号使得表达式成为一个语句。
在我当初解决这个问题的时候,C语言中还没有typedef声明。尽管不用typedef来解决这个问题对剖析本例的细节而言是一个很好的方式,但无疑使用typedef能够使表述更加清晰:
typedef void (*function)();
(*(function)0)();
另外,这贴也写得颇为浅显
http://bbs.chinaunix.net/viewthread.php?tid=993238
分享到:
相关推荐
### C/C++ 函数指针的意义与应用 在C/C++编程中,函数指针是一种高级特性,它允许程序员处理函数的方式如同处理变量一般灵活。理解函数指针的意义及其用法,对于提升代码的可扩展性和灵活性至关重要。 #### 1. ...
在C语言中,指针和函数是两个非常重要的概念,而将它们结合在一起,就产生了指针函数和函数指针变量。这两个概念是C语言高级特性的体现,它们在编程中有着广泛的应用,如回调函数、动态加载库、内存管理等。 首先,...
在C++编程语言中,函数指针是一种强大的特性,它允许程序员通过指针来间接调用函数,从而实现代码的灵活性和复用性。然而,当涉及到类的成员函数时,事情变得稍微复杂一些,因为类的成员函数通常包含一个隐含的参数...
在C/C++编程语言中,函数指针和指针函数是两个不同的概念,虽然它们都涉及到指针和函数,但其用法和含义各有特点。理解这两者的差异对于编写高效、灵活的代码至关重要。 首先,我们来解释"函数指针"。函数指针是一...
### 彻底理解指针,指针数组和数组指针,指针函数和函数指针 #### 一、基础知识 在计算机编程中,指针是一个非常重要的概念,尤其是在C/C++这样的语言中更是如此。简单来说,指针是一种变量,但它存储的不是普通的...
在C++编程中,函数指针是一个非常重要的概念,它允许我们存储函数的地址并可以在程序的不同地方调用。函数指针分为两类:一般(普通)函数指针和类成员函数指针。这两种类型的指针在用法上有所区别,且类成员函数...
Keil C51中函数指针的使用 函数指针是C语言中的一种重要概念,在Keil C51中函数指针的使用具有非常重要的意义。函数指针可以使得程序更加灵活和灵活,特别是在单片机系统中,嵌入式操作系统、文件系统和网络协议栈...
### 函数指针和指针函数的区分及应用 #### 函数指针的理解与使用 **函数指针**是一种特殊的指针类型,它可以用来存储函数的地址,进而通过该指针来调用函数。理解函数指针的关键在于认识到函数也是一种具有特定...
### 函数指针与指针函数的理解 #### 一、函数指针与指针函数的基本概念 在C语言中,函数指针和指针函数是两个重要的概念,它们经常出现在较为复杂的程序设计中,尤其在回调函数、动态绑定等场景中发挥着重要作用。...
C语言指针函数和函数指针详细介绍 C语言中指针函数和函数指针是两个不同的概念,但它们之间存在着紧密的联系。本文将对C语言中的指针函数和函数指针进行详细的介绍。 一、指针函数 指针函数是指返回值为指针的...
3. **取类函数指针的语法**:在易语言中,获取类函数指针通常涉及`__FUNCTION__`关键字,它会返回当前函数的指针,可以用于赋值给类函数指针变量。 4. **类函数的使用场景**:类函数指针在事件处理、异步操作、回调...
【函数指针】是C语言中一种强大的特性,它允许我们使用指针来直接操作函数。函数在程序中被看作是一种数据类型,因此可以像处理其他数据一样处理它们,包括赋值、传递和作为参数。函数指针的定义包含了函数的返回...
C51 函数指针与再入函数 函数指针是 C 语言中几个难点之一。由于 8051 的 C 编译器的独特要求,函数指针和再入函数有更多的挑战需要克服。主要由于函数变量的传递。典型的(绝大部分 8051 芯片)函数变量通过堆栈的...
在C++编程中,函数指针是一个非常重要的概念,它允许我们存储函数的地址并像普通指针一样对其进行操作。函数指针可以被用来作为参数传递给其他函数,或者存储在一个变量中,以便稍后调用。这为程序设计提供了很大的...
函数指针和函数对象 函数指针(全局函数/类成员函数)和函数对象是C++语言中两个重要的概念,经常被混淆和误解。本文将详细介绍函数指针和函数对象的定义、类型、使用方法和区别。 一、函数指针 函数指针是指向...
在C++编程中,函数指针是一个非常强大的工具,它允许我们将函数作为其他函数的参数或者存储在变量中。在本示例中,“函数指针万能打印”着重讲解如何利用函数指针实现一个通用的打印系统,可以适应各种数据类型的...
### 函数指针详解 #### 一、函数指针的概念 在C语言中,函数也是一种特殊的“对象”,它有自己的地址,即函数的入口地址。函数指针是用来存储这些地址的一种特殊类型的指针。通过函数指针,我们可以实现更加灵活的...
在C/C++编程语言中,函数指针是一个非常重要的概念,它允许我们将函数作为其他函数的参数,或者存储在变量中以便稍后调用。在标题"函数指针的使用"中,我们可以深入探讨这个主题。 函数指针的定义: 函数指针是一个...
函数指针的范例 函数指针是 C 语言中的一种高级特性,它允许开发者将函数作为参数传递、返回值或存储在数据结构中。本文将通过一个简单的示例程序,深入探讨函数指针的概念和应用。 首先,让我们从一个简单的函数...
本示例将详细介绍如何将函数指针传递给DLL函数,以便DLL在运行时能够调用应用程序提供的特定功能。 首先,我们需要理解函数指针的概念。函数指针是一个变量,其值为某个函数的地址,允许我们像操作普通数据一样操作...