C 函数指针 函数指针数组 转移表

内容来自《c和指针》,整理后方便个人理解

高级声明

cdel程序可以方便的给出声明的释义

指向函数的指针

int  ( *f ) ( int n_values, float amount );

f is pointer to function returning int

第2个括号是函数调用操作符。第1个括号起到聚组的作用,迫使间接访问在函数调用之前进行,使f成为一个函数指针。f指向的函数返回一个整型值。

int* ( *g ) ( int n_values, float amount );

g is pointer to function returning pointer to int

和上面的大致相同,区别在于g指向的函数返回一个指向整型指针

为了进一步理解“迫使间接访问在…之前进行”,再举例说明

int ( *abc )[6]; /*指向“int类型数组”的指针*/

abc is pointer to array 0…5 of int

先对括号内的*abc求值:迫使间接访问在创建数组之前进行,使abc成为一个指针,abc指向一个数组,数组元素类型是int

指针数组

int* f[10];

f is array 0…9 of pointer to int

两个非法的声明

int f() [];
/*
**__vs2010报错:不允许使用返回数组的函数__
**函数只能返回标量值, 不能返回数组
*/

int f[] ();
/*
**__vs2010报错:不允许使用函数数组__
**数组元素必须具有相同的长度, 但不同的函数显然可能具有不同的长度
*/

函数指针数组

int ( *f[] ) ();

f is array of pointer to function returning int

先对*f[]求值:f是数组,元素类型是某种类型的指针
末尾的()是函数调用操作符
f的元素类型是函数指针,指向的函数的返回值是整型

函数指针

作为参数传递给另一个函数

初始化函数指针

int f( int a );
int ( *pf )( int a ) = &f;
/*
**&可以去掉。因为函数名被使用时总是由编译器把它转换为函数指针。
*/

函数指针的调用

#include <stdio.h>

void f( int a , int* b );

int 
main ( void )
{
	int b;
	void (*pf)( int a, int *b ) = f;
	
	f( 1, &b );
	/*
	**函数名f先被转换为函数指针, 指向函数在内存中的位置, ()调用该函数执行代码
	*/
	(*pf)( 2, &b );
	/*
	间接访问操作符*把pf转换为函数名, 但编译器在执行()之前会把它转换回去
	*/
	pf( 3, &b );
	/*
	**编译器不需要转换直接可以找到函数在内存
	*/
	return 0;
}

void f( int a, int* b )
{
	*b = a;
	printf("%d\n", *b);
}

函数指针应用一 回调函数

callback function

概念:把一个函数指针作为参数传递给其他函数,后者将回调用户的函数。

应用场合:

  • 函数需要在不同时刻执行不同类型的工作
  • 执行只能由函数调用者定义的工作

下面这个例子就是用函数指针实现的可以自定义类型的排序函数sort

#include <stdio.h>
#include <string.h>

void
sort (void *Array, int n_values, int size, int (*compare)( void *a, void *b ) );
/*
**为了能接受不同类型的参数,数组的类型应声明为void*,同时compare指向的函数的参数列表也应如此
*/

int
compare_int ( void *a, void *b );

void
swap ( void *a, void *b );

int
main( int argc, char *argv[] )
{
	
	char a[5] = "dcab";

	int  b[5] = {9,15,2,-1,5};
	int  *p   = b;

	sort( a, strlen(a), sizeof(char), strcmp );\
	/*
	**此处编译器报错的原因是strcmp的参数声明是char*不是void*
	*/
	sort( b, sizeof(b)/sizeof(int), sizeof(int) , compare_int);

	printf("%s\n", a);

	while( p <= &b[4] ){
		printf("%d ", *p++);
	}

	return 0;
}

void
sort( void *Array, int n_values, int element_size, int (*compare)( void *a, void *b ) )
{
	int i, j;
	int cnt;
	int exchange;

	char *ap = ( char* )Array;  /*当成char来处理,把数据类型分解为字节来处理*/

	for( i = 1; i < n_values; i++ ){
		exchange = 1;
		for( j = n_values - 1; j >= i; j-- ){
			if( compare( ap + element_size * j, ap + element_size * (j-1) ) < 0 ){
				/*
				**element_size * j 作为指针的偏移量
				*/
				exchange = 0;
				for( cnt = 0; cnt < element_size; cnt++ ){
					/*
					**cnt = 0...element_size分别对应了数据类型的第一个字节第二个字节
					*/
					swap( ap + element_size * j + cnt , ap + element_size * (j-1) + cnt );
				}
			}
		}
		if(exchange)break;
	}
}

int
compare_int ( void *a, void *b )
{
	if( *( int* )a == *( int* )b ){
		return 0;
	}else if( *( int* )a  < *( int* )b ){
		return -1;
	}else{
		return 1;
	}
}

void
swap ( void *a, void *b )
{
	char tmp = *(char*)a;
	*( char* )a = *( char* )b;
	*( char* )b = tmp;
}

转移表

switch( oper ){
		case 0:
			printf("%lf", ADD( op1, op2 ) );
			break;
		case 1:
			printf("%lf", SUB( op1, op2 ) );
			break;
		case 2:
			printf("%lf", MUL( op1, op2 ) );
			break;
		case 3:
			printf("%lf", DIV( op1, op2 ) );
			break;
}

如果switch操作符的代码是从0开始连续的整数,可以用一个转移表来完成switch语句完成的任务

在这里插入图片描述

	int oper;
	double op1, op2;
	double (*oper_func[])( double op1, double op2 ) = { ADD, SUB, MUL, DIV };
	/*
	**创建一个数组,元素类型是函数指针,指针指向的函数的返回值是double
	*/
	scanf("%lf %d %lf", &op1, &oper, &op2);
	printf("%lf", oper_func[oper]( op1, op2 ));

如果不是从0开始的连续整数,这种情况更合适switch语句。
因为在访问转移表之前,还需要一系列操作把操作符转换为合适的下标。

/*
**不太合适使用转移表的场合
*/
switch( oper ){
		case '+':
			printf("%lf", ADD( op1, op2 ) );
			break;
		case '-':
			printf("%lf", SUB( op1, op2 ) );
			break;
		case '*':
			printf("%lf", MUL( op1, op2 ) );
			break;
		case '/':
			printf("%lf", DIV( op1, op2 ) );
			break;
}

转移表的注意事项

  1. 在转移表,越界访问是十分危险的,测试很难发现bug究竟在哪里,一开始就应该保证转移表使用的下标位于合法的范围内
  2. 使用转移表时应添加注释,以弥补转移表的可读性缺陷
posted @ 2020-05-04 12:29  LanceHansen  阅读(292)  评论(0)    收藏  举报