V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
haozhang
V2EX  ›  C

关于 C 的函数指针指向任意函数类型

  •  1
     
  •   haozhang · 2019-06-17 19:34:55 +08:00 · 3717 次点击
    这是一个创建于 2021 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如果我想要表达一个指向任意函数类型的函数指针的话,用:

    typedef void (*Function)(void);
    Function f = ...;
    

    还是使用:

    typedef void *(*Function)(void *);
    Function f = ...;
    

    又或者用别的什么方式呢?

    29 条回复    2019-06-19 11:25:53 +08:00
    catror
        1
    catror  
       2019-06-17 19:38:50 +08:00   ❤️ 1
    void* 就可以了
    haozhang
        2
    haozhang  
    OP
       2019-06-17 19:42:58 +08:00
    @catror 其实我感觉反正到时候调用函数还是需要强制类型转换的,这里 typedef 写什么都不会影响最终的函数调用= =
    letianqiu
        3
    letianqiu  
       2019-06-17 19:53:56 +08:00
    @haozhang 你的写法根本不能指向任意函数类型。C 里面只有 void *类型表示任意指针类型。你的第一个写法表示的是一个没有参数,没有返回值的函数的指针。第二个写的是一个接受一个指针参数,返回一个指针的函数的指针。
    fcten
        4
    fcten  
       2019-06-17 20:01:33 +08:00
    建议直接用 intptr_t 存,反正使用的时候都要强制转换
    haozhang
        5
    haozhang  
    OP
       2019-06-17 20:01:52 +08:00
    @letianqiu 强制类型转换就可以了啊,比如:
    ```
    int a() {
    return 1;
    }
    Function f = (void (*)(void))a;
    ((int (*)(void))f)() // 返回 1
    ```
    void *是不能指向函数的。
    haozhang
        6
    haozhang  
    OP
       2019-06-17 20:06:10 +08:00
    我二了,试了一下,void *就可以了,直接强制类型转换一下就可以了= =
    int a() {return 1;}
    void *f = (void *)a;
    ((int (*)(void))f)(); // 返回 1
    haozhang
        7
    haozhang  
    OP
       2019-06-17 20:06:33 +08:00
    @letianqiu 我错了,void * 可以指向函数的= =
    zaiyund
        8
    zaiyund  
       2019-06-17 20:08:00 +08:00
    我觉得 catror 的说法靠谱
    letianqiu
        9
    letianqiu  
       2019-06-17 20:11:34 +08:00
    @haozhang void *是可以指向任意指针的,当然包括函数啊。C 里面本来也不存在范型的函数,所以调用的时候都要强制类型转换,void *足够了
    letianqiu
        10
    letianqiu  
       2019-06-17 20:12:43 +08:00
    @haozhang 指针可以直接赋值给 void *类型,不需要强制类型转换。
    haozhang
        11
    haozhang  
    OP
       2019-06-17 20:17:21 +08:00
    @letianqiu 我试了啊,直接不强制类型转换报错的....void *p = xxx_function;
    letianqiu
        12
    letianqiu  
       2019-06-17 20:50:26 +08:00   ❤️ 1
    @haozhang C++下是会报错的。C 是不会报错的
    letianqiu
        13
    letianqiu  
       2019-06-17 21:05:01 +08:00
    https://isocpp.org/wiki/faq/pointers-to-members#cant-cvt-fnptr-to-voidptr
    更正一下,C++下是不能把函数赋值给 void *类型的变量的。
    wutiantong
        14
    wutiantong  
       2019-06-18 00:32:06 +08:00
    @letianqiu 你没有给出正确的参考,因为 pointer to member function 是不同于 pointer to function 的
    wutiantong
        15
    wutiantong  
       2019-06-18 00:36:11 +08:00
    @letianqiu 事实上,C++里 pointer to function 是可以转成 void *的
    wheeler
        16
    wheeler  
       2019-06-18 00:53:22 +08:00 via iPhone   ❤️ 2
    catror
        17
    catror  
       2019-06-18 01:08:23 +08:00
    @haozhang 本质上就是存一个地址值,你强转成任意类型都没问题,只要保证转回来还是一样的就行。建议不要直接用 void*,还是用 typedef 定义一个别名,这样即使遇到 16 楼的链接里提到的情况,修改一下定义就行。
    catror
        18
    catror  
       2019-06-18 01:14:44 +08:00
    @wheeler 第一次知道还有函数指针跟数据指针长度不一样的架构,学习了。
    iwong0exv2
        19
    iwong0exv2  
       2019-06-18 07:18:35 +08:00 via Android
    请参考 dlsym (in Linux) or GetProcAddress (in Windows)
    visionsmile
        20
    visionsmile  
       2019-06-18 08:18:31 +08:00
    @catror #18 C++里成员函数指针和普通的指针不一样,在有些编译器实现里它本质是个结构,存放着真正的函数指针和对 this 的偏移值~
    letianqiu
        21
    letianqiu  
       2019-06-18 09:05:47 +08:00
    @wutiantong 你仔细去看我给出的链接里的例子。C++里将一个函数赋值给 void *类型的指针的结果是为定义的。楼下已经有人提到了,void *只能表示数据类的指针
    zwhfly
        22
    zwhfly  
       2019-06-18 09:20:35 +08:00   ❤️ 1
    各位,void * 类型是不能存储函数指针的。

    https://stackoverflow.com/questions/5579835/c-function-pointer-casting-to-void-pointer

    只谈 C。
    按 C 标准,函数指针可以转换为另一种类型的函数指针,但调用时必须转换到原类型使用。C 标准不支持函数指针与 void 指针互转。
    虽说现实中的大多数编译器支持与 void 指针互转,但这么写是不符合标准的,是不好的。

    回答题主的问题,答案是两种都可以,但第二种没必要,第一种比较干净一点。
    nanmian
        23
    nanmian  
       2019-06-18 09:40:13 +08:00
    1.void* 能不能存函数指针不知道....
    2.单纯提个疑问,调用时的输入输出规则你怎么约定了?

    这样堆栈很容易不平衡的导致程序崩溃的。我曾经在 VS2015_C 语言_Win32 Console_Debug 上测试过,把一种函数指针强转为另外一种指针然后传值调用,如果默认约定是调用者平衡堆栈还好,只是输入输出会取错值(若涉及写形式参数的话,还可能出现"越界访问"),程序不会崩溃。但如果是被调用者平衡堆栈的话,程序会因为堆栈平衡性检查出错而崩溃。
    wutiantong
        24
    wutiantong  
       2019-06-18 10:34:50 +08:00
    @letianqiu 是我 sb 了,其实我昨天打开你那个链接加载得太慢一直没出来页面内位置,然后从上往下翻到了一处讲 pointer to member function 能不能转 void *的,就以为是你给的地方。关于能不能转我好像也搞错了,不好意思哈。
    haozhang
        25
    haozhang  
    OP
       2019-06-18 12:41:35 +08:00 via Android
    结贴,最终我使用了:typedef void (*Function )(void);Function f = ...;
    haozhang
        26
    haozhang  
    OP
       2019-06-18 12:43:34 +08:00
    @nanmian 你强制类型转了之后,调用的时候还是要转换为原来函数的类型啊,不然肯定会出错的。
    12tall
        27
    12tall  
       2019-06-18 17:06:25 +08:00
    void * 可以指向任意数据,但是在使用时一般需要类型转换成相应的类型才可以
    haozhang
        28
    haozhang  
    OP
       2019-06-18 20:31:34 +08:00
    @12tall 回顾下上面的回复,void *无法指向函数指针的。
    12tall
        29
    12tall  
       2019-06-19 11:25:53 +08:00
    @haozhang 谢谢提醒!
    感觉 http://www.c-faq.com/ptrs/generic.html 这个回复更让人信服,不能保证正确的转换;
    StackOverflow 里面的情况感觉和 void * 关系不大。因为之前在调单片机中断时就遇到了有时候需要必要的延时(任意一条语句)才能正确执行,和这个问题很像(跑题了似乎
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2630 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 10:11 · PVG 18:11 · LAX 02:11 · JFK 05:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.