语法—函数声明

Posted lightuup

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了语法—函数声明相关的知识,希望对你有一定的参考价值。

1. 声明和表达式

  任何C变量的声明都由两部分组成:类型 + 一组类似表达式的声明符(declarator)。

  声明符与表达式的相似之处:对它求值应该返回一个声明中给定类型的结果。

  最简单声明单个变量

  如 float f, g; 这个声明的含义是:对其求值时,表达式f和g的类型为浮点数类型,因为声明符和表达式的相似,所以可以在声明符中任意使用括号: float ( (f) );

  同样的逻辑也适用于函数和指针类型的声明。

  float ff( ); 的表达式ff( )求值结果是一个浮点数,也就是说,ff是一个返回值为浮点类型的函数。

  float *pf; 这个声明的含义是*pf是一个浮点数,也就是说,pf是一个指向浮点数的指针。

  以上形式在声明中可以组合起来,就像在表达式中进行组合一样。

  float *g( ), (*h)( ); *g( )与(*h)( )是浮点表达式。因为( )优先级高于*,*g( )也就是*( g( ) ):g是一个函数,该函数的返回值类型为指向浮点数的指针。同理,可以得出h是一个函数指针,h所指向的函数的返回值为浮点类型。

2. 声明和类型转换

  一旦我们知道了如何声明一个给定类型的变量,那么该类型的类型转换符就很容易得到了:

  只需要把声明中的变量名和声明末尾的分号去掉,再将剩余的部分用一个括号整个”封装“起来即可。

  例如,有如下声明:float (*h)( ); 表示h是一个指向返回值为浮点类型的函数的指针。

  因此,( float (*)( ) ) 表示一个”指向返回值为浮点类型的函数的指针“的类型转换符。

3. 分析( *(void(*)())0 )( ) ,调用首地址为0位置的子例程

  第一步:

  假定变量 fp 是一个函数指针,那么如何调用 fp 所指向的函数呢?

  (*fp)( );

  因为 fp 是一个函数指针,那么 *fp 就是该指针所指向的函数,所以(*fp)( )就是调用该函数的方法。不建议简写为 fp( )。

  第二步:

  (*0)( ); 能不能这样写呢?不行,因为运算符必须要一个指针来做操作数。而且,这个指针还应该是一个函数指针,这样经运算符 * 作用后的结果才能作为函数调用。

  因此,上式必须对 0 做类型转换,转换后的类型可以大致描述为:”指向返回值为void类型的函数的指针“。(因为子例程返回值为void)

  如果 fp 是一个指向返回值为void类型的函数的指针,那么(*fp)( )的值为void,fp的声明如下:void (*fp)( );

  因此,我们可以用下式来完成调用存储位置为0的子例程:

  void (*fp)( );  //先声明一个”指向返回值为void类型的函数的指针“

  (*fp)( );  //调用该函数指针

  这里默认fp初始化为0,这种写法不提倡。且多声明了一个”哑“变量。

  一旦知道如何声明一个变量,也就知道了如何对一个常数进行类型转换,将其转型为改变了的类型:( void (*)( ) ) 0 。

  因此,可以用( void (*)( ) ) 0 来替换fp,从而得到:( *(void(*)())0 )( ) ;   。

  末尾的分号使得表达式成为一个语句。

  使用typedef能够使表述更清楚:

  

  typedef void (*funcptr)( );
  ( *(funcptr)0 )( );

  

4. 考虑signal函数

  在包含signal函数的C编译器实现中,signal函数接受两个参数:一个是代表需要”被捕获“的特定signal的整数值;另一个是指向用户提供的函数的指针,该函数用于处理”捕获到“的特定signal,返回值类型为void。

  一般情况下,程序员并不主动声明signal函数,而是直接使用系统头文件signal.h中的声明。那么,signal函数是责骂声明的呢?

  首先考虑用户定义的信号处理函数

  该函数可以定义如下:

1 void sigfunc( int n )  //参数n是一个代表特定信号的整数值
2 {
3     /*特定信号处理部分*/
4 }

   sigfunc函数的声明可以如下:void sigfunc( int );

  现在假定我们需要一个指向sigfunc函数的指针变量,不妨命名为sfp。*sfp则代表了sigfunc函数,因此*sfp可以被调用。

  又假定sig是一个整数,则(*sfp)(sig)的值为void类型,因此可如下声明sfp:

1 void (*sfp) ( int );

   因为signal函数的返回值类型与sfp的返回类型一样,上式也就声明了signal函数,我们可以如下声明:

1 void (*signal ( something )) (int); 

   此处的something代表了signal函数的参数类型。上面声明可以这样理解:

  1. 传递适当的参数以调用signal函数;

  2. 对signal函数返回值(为函数指针类型)解处引用(dereference);

  3. 然后传递一个整型参数调用引用解除后所得的函数;

  4. 最后返回值为void类型。

  因此,signal函数的返回值是一个指向返回值为void类型的函数的指针

  那么,signal函数的参数又如何呢?signal函数接受两个参数:一个整型的信号编号,以及一个指向用户定义的信号处理函数的指针(即此前定义的sfp)。

  sfp的类型可以通过将上面的声明中的sfp去掉而得到,即void (*)(int)。此外,signal函数的返回值是一个指向调用前的用户定义信号处理函数的指针,这个指针类型和sfp指针类型一致。

  因此,可以如下声明signal函数:

1 void ( *signal( int,void (*) ( int )) ) ( int );

   signal( int,void (*) ( int )) 是一个指向返回值为void类型的函数的指针

  同样的,使用typedef可以简化上面的函数声明:

1 typedef void (*HANDLER) ( int );
2 HANDLER signal( int, HANDLER );

 

 

  

以上是关于语法—函数声明的主要内容,如果未能解决你的问题,请参考以下文章

inline内联函数

如何让片段中的多个视图调用片段类中声明的相同 onClick 函数?

写出java中定义方法的语法格式,并说明一个方法可以有多少个参数,多少个返回值?

未捕获的语法错误:在严格模式代码中,函数只能在顶层或立即在另一个函数中声明

JSP标签语法JSTL标签库EL表达式辨析

JSP 语法