内容来自《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. 使用转移表时应添加注释,以弥补转移表的可读性缺陷

C 函数指针 函数指针数组 转移表的更多相关文章

  1. C/C++用状态转移表联合函数指针数组实现状态机FSM

    状态机在project中使用很的频繁,有例如以下常见的三种实现方法: 1. switch-case 实现.适合简单的状态机. 2. 二维状态表state-event实现.逻辑清晰.可是矩阵通常比較稀疏 ...

  2. C语言函数指针变量和指针函数以及指针数组

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

  3. CPP-基础:函数指针,指针函数,指针数组

    函数指针 函数指针是指向函数的指针变量. 因而“函数指针”本身首先应是指针变量,只不过该指针变量指向函数.这正如用指针变量可指向整型变量.字符型.数组一样,这里是指向函数.如前所述,C在编译时,每一个 ...

  4. C与指针(结构体指针,函数指针,数组指针,指针数组)定义与使用

    类型 普通指针 指针数组(非指针类型) 数组指针 结构体指针 函数指针 二重指针 定义方式 int *p; int *p[5]; int (*p)[5]; int a[3][5]; struct{.. ...

  5. c/c++ 函数指针 指针函数 数组的引用 指针数组 数组指针

    1.指针数组数组指针 引用数组 数组的引用 int *a[10] 指针数组 每一个元素都是一个指针 Int (*a)[10] 数组指针 P指向一个含有10个元素的数组 Int (&a)[10] ...

  6. PHP中使用数组指针函数操作数组示例

    数组的内部指针是数组内部的组织机制,指向一个数组中的某个元素.默认是指向数组中第一个元素通过移动或改变指针的位置,可以访问数组中的任意元素.对于数组指针的控制PHP提供了以下几个内建函数可以利用. ★ ...

  7. C/C++ 一段代码区分数组指针|指针数组|函数指针|函数指针数组

    #include<stdio.h> #include<stdlib.h> #include<windows.h> /* 举列子说明什么是函数指针 */ //以一个加 ...

  8. 指针数组vs数组指针 指针函数vs函数指针

    在分辨这些重要的概念时,我们先回顾一下前面所讲的C之三值合一,由于三个值所求出的地址是相同的,所以经常有传言说他们都是首元素的地址.这种说法是不正确的.为什么说它是不正确的呢? 首先定义一个指针,将三 ...

  9. C语言基础知识点整理(函数/变量/常量/指针/数组/结构体)

    函数 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ...

随机推荐

  1. Ecplise项目导入IDEA(纯小白名词解释)

    1. Module 模块 一个大的项目不仅仅是只有Java的源文件,还有数据库,服务器,web等等文件一起使用,将类似于这样分类的文件定义为 module 例如 core Module(核心).web ...

  2. 前端性能优化(四)——网页加载更快的N种方式

    网站前端的用户体验,决定了用户是否想要继续使用网站以及网站的其他功能,网站的用户体验佳,可留住更多的用户.除此之外,前端优化得好,还可以为企业节约成本.那么我们应该如何对我们前端的页面进行性能优化呢? ...

  3. NOIP模拟「random·string·queen」

    T1:random   我又来白剽博客了:   详细证明请看土哥   土哥写的不是很详细,我在这里详细写一下:   首先,对于f[n]的式子:   加一是那一个对的贡献,大C是选其余的几个数,\(2^ ...

  4. 安装 Ubuntu 21.04 后必备的绝佳应用大合集(持续更新中)

    @ 目录 一.Google Chrome 浏览器 1.下载 2.安装 3.设置搜索引擎 二.火焰截图(替代QQ截图) 1.简介: 2.安装: 3.设置快捷键: 三.VLC视频播放器(替代Potplay ...

  5. python3 爬虫五大模块之五:信息采集器

    Python的爬虫框架主要可以分为以下五个部分: 爬虫调度器:用于各个模块之间的通信,可以理解为爬虫的入口与核心(main函数),爬虫的执行策略在此模块进行定义: URL管理器:负责URL的管理,包括 ...

  6. Python - poetry(4)管理环境

    环境隔离 poetry 核心之一:使项目环境隔离,意味着始终和本地全局 Python 环境隔离 poetry 首先会检查当前项目是否在虚拟环境中运行:如果是将直接使用它,而不创建新的:如果不是,poe ...

  7. [源码解析] PyTorch 流水线并行实现 (1)--基础知识

    [源码解析] PyTorch 流水线并行实现 (1)--基础知识 目录 [源码解析] PyTorch 流水线并行实现 (1)--基础知识 0x00 摘要 0x01 历史 1.1 GPipe 1.2 t ...

  8. Linux系列(42) - 防火墙相关命令

    # 开启 service firewalld start # 重启 service firewalld restart # 关闭 service firewalld stop # 查看防火墙规则 fi ...

  9. python二级 之 第 五套

    1. 这里要注意输入的   就是列表 .                 [1,2,3] 2. 就是你要明白   random.seed()  产生随机种子# 与random.randint()  取 ...

  10. 鸿蒙内核源码分析(GN应用篇) | GN语法及在鸿蒙的使用 | 百篇博客分析OpenHarmony源码 | v60.01

    百篇博客系列篇.本篇为: v60.xx 鸿蒙内核源码分析(gn应用篇) | gn语法及在鸿蒙的使用 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙 ...