学习并弄乱了函数指针,我注意到了一种初始化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)
这里有两个严重的问题:
void *)的转换会触发未定义的行为:原则上,它可能会使系统崩溃(尽管在实践中有许多系统可以正常工作).而不是void *,最好为此目的使用函数指针类型.int给一个期望a的函数uint8_t.这也是未定义的行为,而且非常危险.由于编译器不知道它正在这样做,它甚至不能采取最基本的必要步骤来避免粉碎堆栈 - 你真的在这里赌博.类似地,这是一个更微妙的,但你也欺骗编译器将两个int-s 传递给一个期望两个uint16_t-s 的函数.还有两个较小的问题:
typedef void (*any_func_ptr)(); any_func_ptr foo = (any_func_ptr)(bar).