语法—函数声明

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 );

  

原文地址:https://www.cnblogs.com/lightuup/p/10612021.html

时间: 2024-10-11 02:59:36

语法—函数声明的相关文章

JavaScript函数声明与函数表达式

定义函数的方式有两种:一种是函数声明,另一种是函数表达式. 一.两者的语法 函数声明的语法: function functionName(arg0, arg1, arg2){ //函数体 } 函数表达式的语法: var functionName = function(arg0, arg1, arg2){ //函数体 } 因为function关键字后面没有标识符,这种情况下创建的函数叫做匿名函数(anonymous function,有时也叫拉姆达函数).匿名函数的name属性是空字符串. 二.两

Scala基础语法(声明定义、标识符命名、Scala脚本入门)

一.声明与定义(赋值) 1.val, 常量声明   val x:T val x:T=e 2.var, 变量声明   var x:T var x:T=e ?类型省略(默认类型)val x=e var x=e ? 声明省略(连续声明) val x1,x2,x3 等价于 val x1;val x2; var x1,x2,x3:T=e  等价于  var xn:T=e 3.def,函数声明def abc(xn:T):T*=e def adder(m:Int,n:Int) =m+n def adder()

函数声明

语法 function name([param,[, param,[..., param]]]) { [statements] } name 函数名 param 传递非函数的参数名,参数个数最多255个. statements 构成函数体的语句. 描述 通过函数声明构造的函数是Function对象,所以拥有一切Function对象所有的属性,方法和行为. 函数默认返回undefined,如果想返回其他值,函数必须使用return语句来返回你想返回的值. 通过条件语句判断创造的函数 函数可以通过条

Javascript中函数声明与函数表达式的不同

定义函数的方式有两种:一种是函数声明,另一种是函数表达式. 函数声明的语法如下: 1 function functionName(arg0,arg1,arg2){ 2 //函数体 3 } 函数表达式的语法如下: 1 var functionName = function(arg0,arg1,arg2){ 2 //函数体 3 } 两者的区别: 函数声明的特征就是函数声明提升,即执行代码之前js解析器会先读取函数声明,所以如下的代码不会提示错误. 1 sayHi(); 2 function sayH

javascript中函数声明和函数表达式浅析

记得在面试腾讯实习生的时候,面试官问了我这样一道问题. //下述两种声明方式有什么不同 function foo(){}; var bar = function foo(){}; 当初只知道两种声明方式一个是函数声明一个是函数表达式,具体有什么不同没能说得很好.最近正好看到这方面的书籍,就想好好总结一番. 在ECMAScript中,有两个最常用的创建函数对象的方法,即使用函数表达式或者使用函数声明.对此,ECMAScript规范明确了一点,即是,即函数声明 必须始终带有一个标识符(Identif

详解函数声明VS函数表达式

函数声明 比方如下:1.我们以一个完整的语句以function开头,不加任何东西. 2.有一个函数名(add) 3.参数可带可不带(x,y) 4.有一个数体 满足以上要求的我们统称为函数声明! 附加小知识点: 定义和用法 isNaN() 函数用于检查其参数是否是非数字值. 语法 isNaN(x) <body> <script> function add(x,y){ if(isNaN(x) || isNaN(y)){ return //不执行任何东西 } return x+y //上

Javascript函数声明与函数表达式的区别

在定义函数时,我们一般使用下面这两种方法: 使用函数声明定义: 1 2 3 function  sum (a, b) {     return a + b; } 使用函数表达式定义: 1 2 3 var sum = function (a, b) {     return a + b; } 调用方法都是一样的: 如求"1+1"等于几: 1 alert(sum(1, 1)); 但这两种方法还是有区别的.解析器在向执行环境中加载数据时,对函数声明和函数表达式并非一视同仁.解析器会率先读取函

JavaScript 函数声明,函数表达式,匿名函数的区别,深入理解立即执行函数(function(){…})()

function fnName(){xxxx}; // 函数声明:使用function关键字声明一个函数,在指定一个函数名. //例如:(正常,因为 提升 了函数声明,函数调用可以在函数声明之前) fnName(); function fnName(){ alert('Hello World'); } var fnName = function(){xxxx}; //函数表达式:使用function关键字声明一个函数,但是未给函数命名,最后将匿名函数赋予给一个变量. //例如1:(报错,变量fn

深入理解变量声明提升和函数声明提升

变量声明提升 1.变量定义 可以使用var定义变量,变量如果没有赋值,那变量的初始值为undefined. 2.变量作用域 变量作用域指变量起作用的范围.变量分为全局变量和局部变量.全局变量在全局都拥有定义:而局部变量只能在函数内有效. 在函数体内,同名的局部变量或者参数的优先级会高于全局变量.也就是说,如果函数内存在和全局变量同名的局部变量或者参数,那么全局变量将会被局部变量覆盖. 所有不使用var定义的变量都视为全局变量 3.函数作用域和声明提前 JavaScript的函数作用是指在函数内声