这是一个通用函数指针,它是危险的吗?

D.N*_*uko 8 c pointers

学习并弄乱了函数指针,我注意到了一种初始化void函数指针并转换它们的方法.然而,虽然我没有收到任何警告或错误,无论是使用GCC还是VS的编译器,我都想知道这样做是危险的还是坏的做法,因为我没有看到这种方法通常在互联网.而且,我们称之为通用函数指针吗?

#include <stdio.h>
#include <stdint.h>
#include <conio.h>

#define PAUSE (_getch())

uint16_t add(const uint16_t x, const uint16_t y) {
    return x + y;
}

char chr(uint8_t test) {
    return (char)test;
}

int main(void) {

    void(*test)() = (void*)add;

    const uint16_t x = 1, y = 1;
    uint16_t value = ((uint16_t(*)())test)(x, y);

    test = (void*)chr;

    printf("%d\n", add(x, y));                    // 2
    printf("%d\n", value);                        // 2
    printf("%c\n", ((char(*)())test)(100));       // d

    PAUSE;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

小智 8

这是一个通用函数指针

不,如果我不是非常错误,那么在C中就没有"泛型函数指针"这样的东西.

这是危险的吗?

是的.这是邪恶的.


您需要了解一些事项.首先,除非您运行的系统符合POSIX,

void(*test)() = (void*)add;
Run Code Online (Sandbox Code Playgroud)

错的. void *是指向对象的指针类型,因此它与函数指针不兼容.(至少在标准C中没有 - 正如我所提到的,POSIX要求它也与函数指针兼容.)

第二件事是,void (*fp)()并且void (*fp)(void)是不同的.前一个声明允许fp获取任何类型的任意数量的参数,并且当编译器看到对函数(指针)的第一次调用时,将推断出参数的数量及其类型.

另一个重要的方面是保证函数指针可以相互转换(AFAIK这表明它们具有相同的表示和对齐要求).这意味着任何函数指针都可以分配给任何函数的(地址)(在适当的强制转换之后),只要你不通过指向不兼容类型的指针调用函数.当且仅当您在调用指针之前将指针强制转换回原始类型时,行为才是明确定义的.

所以,如果你想要一个"通用"函数指针,你可以写一些像

typedef void (*fn_ptr)(void);
Run Code Online (Sandbox Code Playgroud)

然后你可以将任何指向函数的指针分配给一个类型的对象fn_ptr.您需要注意的是,在调用函数时,再次转换为正确的类型,如:

int add(int a, int b);

fn_ptr fp = (fn_ptr)add; // legal
fp(); // WRONG!
int x = ((int (*)(int, int))fp)(1, 2); // good
Run Code Online (Sandbox Code Playgroud)


rua*_*akh 5

这里有两个严重的问题:

  1. 从函数指针到对象指针(例如void *)的转换会触发未定义的行为:原则上,它可能会使系统崩溃(尽管在实践中有许多系统可以正常工作).而不是void *,最好为此目的使用函数指针类型.
  2. 你在不知不觉中将编译器传递int给一个期望a的函数uint8_t.这也是未定义的行为,而且非常危险.由于编译器不知道它正在这样做,它甚至不能采取最基本的必要步骤来避免粉碎堆栈 - 你真的在这里赌博.类似地,这是一个更微妙的,但你欺骗编译器将两个int-s 传递给一个期望两个uint16_t-s 的函数.

还有两个较小的问题:

  1. 函数指针类型的表示法(例如,在演员表中)是令人困惑的.我认为使用typedef更好:typedef void (*any_func_ptr)(); any_func_ptr foo = (any_func_ptr)(bar).
  2. 调用具有与实际函数不同的签名的函数指针是未定义的行为.您可以通过仔细编码来避免这种情况 - 比您当前的代码更加谨慎 - 但这很棘手且风险很大.