神奇的数组与函数指针_fltused函数的功能-程序员宅基地

技术标签: struct  div  ASM/WTL/MFC/QT  语言  编译器  存储  class  

前段时间,关于梯形图转指令表确实让人蛋疼(实际上到现在还没“消停”),因为“他”的转换使用了“函数指针”。比如常见的LD、LDI、OR、ORI、OUT、AND等等指令,在构造指令表的时候,有没有想过自己的switch,比如大量的case......

 

(扯远了,回正题目。)

 

先参考文章:http://blog.chinaunix.net/u/26710/showart_209517.html

 

c 函数指针   
    以前什么函数指针,指针函数仅仅是概念上的理解,最近写个程序有这么个需求,其实也不是有这么个需求,而是这样写可以省去很多事....search了下,找到一篇好文。

函数指针是什么?
    先来看函数调用是怎么回事。一个函数占用一段连续内存。当调用一个函数时,实际上是跳转到函数入口地址,执行函数体的代码,完成后返回。如何找到对应的入口地址?这是由函数名来标记的,实际上,函数名就是函数的入口地址。
    函数指针是一种特殊类型的指针,它指向一个函数的入口地址。

    注意:除了void类型指针是无类型的指针外,其他所有指针都是有对应类型的,例如int *pint、struct studentdata *psdata等,只有指明了指针所指的数据类型,编译器才能为指针分配或预计分配相应大小的存储空间,指针的算术运算如pint++等才是有意义的。因此,定义了某种类型的指针之后,除非使用强制类型转换,那么它只能指向相应数据类型的变量或常量,不同类型的指针或数据之间不可混用。所以指针的类型实际上是一种身份标志的作用。

   函数指针如何表明自己的身份呢?为了避免混乱,必须也要作出相应规定,不同函数的函数指针不能混用。例如,int func1(int arg11, char arg12)与int func2(char arg)的函数指针就不能混用,要定义可以指向func1的函数指针应该这样:
    int (*pfunc1)(int, char) = func1;
定义可以指向func2的函数指针则该如下:
    int (*pfunc2)(char) = func2;
    从函数指针的定义可以看出,函数指针的类型实际上是由函数签名决定的。函数签名就象是函数的身份证,一个函数的函数签名是独一无二的,具有相同函数签名的函数实际上就是同一函数。函数签名包括函数名、函数形参类型的有序列表和函数返回值类型。

    一个函数指针的定义规定了它只能指向特定类型的函数。如果两个函数的形参列表和返回值类型相同,只有函数名和函数体不同,则可以使用相同类型的函数指针。例如,如果还有一个函数int func3(char arg),则上面定义的可以指向函数func2的函数指针也可以用于指向func3,即:
    pfunc2 = func3;
    再使用pfunc2(char ARG)就可以调用函数func3,这时指令计数器(PC)指向函数入口,从此开始执行函数体代码。
    注意:对函数指针进行算术运算也是没有意义的。

如何使用函数指针?
    ①定义合适类型的函数指针变量;
       int (*pfunc)(int, int);
    ②给函数指针变量赋值,使它指向某个函数入口;
       int example(int, int);
       pfunc = example;          /*将函数入口地址赋给函数指针变量*/
       或者:pfunc = &example;    /*函数名总是被编译器转换为函数指针(入口地址)来使用,因此与上面一句等价 */
    ③使用函数指针来调用相应的函数;
       retval = pfunc(10, 16);
       或者:retval = (*pfunc)(10, 16);
       上面两句都与retval = example(10, 16);等价。
    理解:一个指针变量p实际上也和普通的变量一样,要占存储空间(通常与平台的虚拟地址一样宽),也有其自身的存储地址&p;不同的是,在指针变量 p的值有特殊的意义,它是另外一个变量或常量的地址值,也就是说,在地址为&p的存储单元上存放着另外一个数据的地址。因此,*p实际上是将p看作它指向的数据的地址来使用,*操作符是引用相应地址中的数据,也就是对地址为p的存储单元中存放的数据进行操作。
    一个函数指针变量则更为特殊。比如上面的例子,pfunc变量本身的值是函数example()的入口地址。因此pfunc可以代替其所指函数的函数名来使用。至于*pfunc,如果按照上面的理解,它实际上是地址pfunc的内容,也即函数example()的入口地址的内容,就有点含糊了。不过,从另一方面来理解,如果使用pfunc = &example来初始化pfunc,则*pfunc == *(&example) == example,又与pfunc等价。因此,就有了两种使用函数指针来调用相应函数的形式。
    值得注意的是,不可用*pfunc来对pfunc的值初始化。即*pfunc = example的写法是错误的。

为什么要使用函数指针?
    前面介绍了函数指针的基本知识和使用规范。下面介绍函数指针的实际用途。不过首先要对前面的知识再做一个补充,因为下面的应用很可能用到这一特性。前面指出,除函数名之外的函数签名内容(函数返回值类型和形参列表)决定了函数指针的类型。实际上还有一种特殊的或说通用的函数指针,在定义这类函数指针时,只需要指定函数返回值类型,而留空形参列表,这样就可以指向返回值类型相同的所有函数。例如:
    int (*pfunc)();
    这样定义的pfunc就可以指向前面提到的func1和func2,因为他们都返回整型值。
    注意: int (*pfunc)()与int (*pfunc)(void)不是一回事,后者不允许接受任何参数。

    函数指针最常见的三个用途是:
    ①作为参数传递给其他函数。
    这样可以把多个函数用一个函数体封装起来,得到一个具有多个函数功能的新函数,根据传递的函数指针变量值的不同,执行不同的函数功能。这是函数嵌套调用难以实现的。参数的传递可以由程序员设定,也可以由用户输入读取,因此具有较大的灵活性和交互性。
    另外还可以用于回调函数。使用void配合,还可以将对不同数据类型的数据进行相同处理的多个函数封装为一个函数,增强函数的生命力。

    ②用于散转程序。
    这种程序首先建立一个函数表(实际上是一个函数指针数组),表中存放了各个函数的入口地址(或函数名),根据条件的设定来查表选择执行相应的函数。这样也可以将多个函数封装为一个函数或者程序,散转分支条件可以由程序员设定,也可以由用户输入读取,甚至是外设的某种特定状态(这种状态可以是不受人为控制的)。

    ③实现C的面向对象的类的封装。
    C语言中的struct与C++中的class有很大不同,除了缺省的成员属性外(struct的成员缺省为public的,可随意使用,而class成员缺省为private的),struct还很难实现类成员函数的封装。struct的成员一般都是数据成员,而非函数成员。因此,为了在C语言中,为某个struct定义一套自己的函数对结构数据成员进行操作,可以在struct结构体中增加函数指针变量成员,在初始化时使它指向特定函数即可。

 

应用举例:
    ①假设定义了四个函数 :add(int, int)、sub(int, int)、mul(int, int)、div(int, int),可以将其封装为一个四则运算计算器函数:
    double calculator(int x, int y, int (*pfunc)(int, int)) {
       double result;
       result = pfunc(x, y);
       return result;
    }

    又例如,在一个链表查询程序中,要通过比较节点的特征值来查询节点,不同类型的数据的比较方式不一样,整型等可以直接比较,字符串却要用专门的字符串操作函数,为了使代码可重用性更高,可以使用一个比较函数来代替各种不同数据类型的直接比较代码,同时,比较函数也必然是数据类型相关的,因此要使用void 指针和函数指针来转换为类型无关的比较函数,根据相应的数据类型,调用相应的函数(传递相应的函数指针)。一个实例是:
    int (*compare)(void const *, void const *);
    这个函数指针可以接受任意类型的数据的指针参数,同时返回int值作为比较结果标志。一个比较整型数据的比较函数是:
    int compare_ints(void const *a, void const *b) {
       if( *(int *)a == *(int *)b ) 
          return 0;
       else
          return 1;
    }

    ②散转程序 。通过一个转移表(函数指针数组)来实现。还是上面定义的四个四则运算函数,可以建立这样一个转移表(注意初始化该转移表的语句前面应有add等相应函数原型声明或定义):
    double (*calculator[])(int, int) = {
       add, sub, mul, div
    };

    这样,calculator[0] == add, calculator[1] == sub, ...
    使用result = calculator[oper](x, y) ;就可以代替下面整个switch语句:
    switch( oper ) {
       case 0: result = add(x, y); break;
       case 1: result = sub(x, y); break;
       ...
    }


    ③C的面向对象化 。一个对象包括数据和对数据的操作。C语言中的struct只有数据成员,因此要增加一些“伪数据成员”即函数指针来实现对数据的操作。例如:
    #ifndef C_Class
    #define C_Class struct
    #endif
    C_Class student{
       C_Class student *student_this
       char name;
       int height;
       int gender;
       int classnum;
       ...
       void (*Oper)( C_Class student *student_this );
       ...
    }

 

 

参考大牛的文章,于是自己尝试的写下了下面的C++代码,有兴趣的看看!

 

cl /FA point.cpp

 

查看生成的汇编代码:

 

由于水平有限,哪里不正确的话,希望大牛能及时纠正,谢谢~~~

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wangningyu/article/details/5455378

智能推荐

Julia 并发编程 ---- @distributed 和 pmap的区别-程序员宅基地

文章浏览阅读1.2k次。目录1、功能说明2、代码用例2.1 使用 @distributed2.2 使用pmap3、总结1、功能说明@distributed(Julia 0.6之前用@parallel)会立即把要做的工作平均分配给所有的workers。注意,在@distributed中,会在指定范围在根据所有的worker的个数分片。相比之下,pmap将启动每个worker的工作,并根据w...

UE4如何检测目标在锥形视野内_unreal 计算目标是否在视锥体内-程序员宅基地

文章浏览阅读9.4k次,点赞5次,收藏15次。做UE4游戏AI方面经常会遇到一个问题,就是何如判定目标在AI单位的视野范围内,假如我们现在要检测玩家在AI单位的前方60°夹角的视野范围内,如果在的话就把玩家设置为该AI单位的目标。我做了一个简单的Service节点来处理,如图这两大图可能看不清,我把图分开又截了2张。 当然这个简单节点中没有检测两者之间的距离,如果要实际应用肯定还要加..._unreal 计算目标是否在视锥体内

latex排版笔记_winedt 11-程序员宅基地

文章浏览阅读459次。latex写的格式不需要自己调,和编程差不多。关于latex和winedt的关系请参考百度:winedt 定义:  WinEdt软件是一个Windows平台下的强大的通用文本编辑器,其更倾向于LaTeX/TeX文档的编辑  latex定义:  LaTeX(LATEX,音译“拉泰赫”)是一种基于ΤΕΧ的排版系统,由美国计算机学家莱斯利·兰伯特(Leslie Lamport)在20世纪80_winedt 11

TabLayout ViewPager fragment 联动_tablayout fragment绑定后如何联动 java android-程序员宅基地

文章浏览阅读245次。首先先看效果图:具体实现:首先导入依赖包:compile 'com.android.support:design:27.+'布局文件:<?xml version="1.0" encoding="utf-8"?><RelativeLayout ="http://schemas.android.com/apk/res/android" xmlns:..._tablayout fragment绑定后如何联动 java android

ffmpeg 编码生成mp4文件大小 码率控制_ffmpeg保存mp4设置码率-程序员宅基地

文章浏览阅读1.2w次,点赞6次,收藏14次。AVCodecContext* pCodecCtx = m_stVideoStream.pStream->codec; JP_ASSERT(NULL != pCodecCtx); pCodecCtx->codec_id = eVideoCodecId; pCodecCtx->gop_size = 1_ffmpeg保存mp4设置码率

一文看懂python的迭代器和可迭代对象_一文看懂python的迭代器和可迭代对象_酷python的博客-csdn博客 0阅读1970年01月-程序员宅基地

文章浏览阅读264次。阅读完本文,你将收获以下知识点:什么是迭代器和可迭代对象,他们之间有什么关系迭代器可以迭代的底层原理是什么如何自定义可迭代对象和迭代器迭代器和可迭代对象是两个非常难以搞懂的python概念,我试图用最轻松的方式为你揭开它们的神秘面纱,你也将在这个过程中学习到研究技术的方法。1. 判断一个对象是不是可迭代对象,是不是迭代器我研究的思路,第一步先找到一种办法,可以判断一个对象是不是可迭代对象,是不是迭代器。如果我掌握了这种方法,那么我就可以用它来识别哪些是可迭代对象,哪些是迭代器,这样做非常关_一文看懂python的迭代器和可迭代对象_酷python的博客-csdn博客 0阅读1970年01月01

随便推点

10.10上下文菜单与上下文操作模式。_10、控制上下文菜单和粘贴-程序员宅基地

文章浏览阅读659次。目的:为应用实现长按列表项删除crime记录功能。3.0版本前(旧)是在浮动上下文菜单实现3.0版本后(新)在上下文操作栏呈现。为兼容API级别,必须定义一种菜单资源,和两组回调方法(新旧各一种)。18.2.1菜单资源:在res/menu中新建菜单资源文件。 android:icon="@android:drawable/ic_menu_dele_10、控制上下文菜单和粘贴

python如何将列表中的每个数字都保留两位有效数字_python中的使得表格某列的数字全部保留两位小数-程序员宅基地

文章浏览阅读2.2w次,点赞21次,收藏45次。如何将list中的每个数字都保留两位有效数字关键:首先,将list转为numpy数组,然后对numpy进行操作,最后对操作完成的numpy再转为数组。直接附代码list_ori = list(pixel_real_recognition_value) #原始列表mid_np = np.array(list_ori) #列表转数组mid_np_2f =..._python中的使得表格某列的数字全部保留两位小数

native层实现touch事件转key事件_touchinputmapper::dispatchmotion-程序员宅基地

文章浏览阅读572次。需求 最近公司来了一个需求,需要将touch事件转成key事件,只针对滑动事件与触摸事件。需求分析 首先这个需求是可以在kernel里面做的,由于我们没有kernel代码,因此这个方案就被pass掉了。第二个想到的是在java层做,想找一个拦截touch事件的方法,类似PhoneWindowMananger中拦截key事件的方法,可是没找到,找到的地方都已经到app层了,这样就可能有点..._touchinputmapper::dispatchmotion

python视图ajax请求,python之 使用 flask Blueprint(蓝图) 接收前台的ajax的post请求,报405 METHOD NOT ALLOWED错误的解决办法...-程序员宅基地

文章浏览阅读516次。在利用flask进行python的项目的开发过程中,做到了注册这一块,在前台利用ajax+post请求的时候,报了405 METHOD NOT ALLOWED的错误。网上的解决办法乱搜了一通,试了好久,均没有解决405 METHOD NOT ALLOWED这个问题。image.png和报错相关的文件代码(passport.py文件)如下@api.route("/users", methods=['..._flask 错误处理 ajax

react-monaco-editor使用_react-monaco-editor无法实现代码高亮-程序员宅基地

文章浏览阅读5k次。1.版本"react": "^17.0.2","@craco/craco": "^6.2.0","monaco-editor": "0.27.0","monaco-editor-webpack-plugin": "4.1.2",2.使用react-monaco-editor代码import React, { useState, useRef } from 'react';import MonacoEditor from 'react-monaco-editor';function ME_react-monaco-editor无法实现代码高亮

6a828下android5.0下usb触摸屏SHOW_TOUCHES有轨迹会导致多次点击或双点系统没反应_show touch enable-程序员宅基地

文章浏览阅读1k次。如果SHOW_TOUCHES为false那么多次点击双点系统都没问题如果SHOW_TOUCHES为true那么设备在getevent正常logcat也有东西就是系统点击没有反应 遥控器也打印单没反应 界面停止不动 am start 也不好使getevent -p发现 mstarpitayaBoardConfigCommonmk144ENABLE_HWCURSOR true undef ENA_show touch enable

推荐文章

热门文章

相关标签