如何通过预处理器将外部函数指针转为外部函数(不改库文件)
是的,这个需求是可行的,但需要结合宏技巧和链接器手段,或者用一个更稳妥的替代方案来达成你的目标——既避免误修改全局变量,又能接近直接调用函数的性能。
先拆解你的场景:库的代码硬编码了函数指针声明extern void (*some_func)();和调用逻辑(some_func)();,你希望把它改成直接调用外部函数some_func(),省掉指针间接跳转的开销,同时防止他人篡改这个全局指针。
方法一:链接器符号别名(完全实现你的需求)
这个方法能让库直接调用你的函数,彻底去掉函数指针变量:
- 在
my_file.h中添加宏替换,把库中的some_func指向你的实际函数:
#define some_func some_func_real
- 在你的代码里实现目标函数:
void some_func_real() { // 你的业务逻辑 }
- 编译库时,给编译器添加链接器参数,把库中引用的
some_func(函数指针变量符号)别名到你的函数some_func_real。比如GCC/Clang下可以用:
-Wl,--defsym=some_func=some_func_real
注意:部分链接器可能会因为数据符号(变量)和代码符号(函数)类型不同报错,这时可以用一个const指针过渡:
void some_func_real() { /* ... */ } // 用const修饰防止误修改,编译器会自动优化掉间接跳转 void (*const some_func)() = some_func_real;
这样既满足库对指针的依赖,又锁死了指针不能被修改,性能几乎和直接调用函数一致。
方法二:纯宏技巧(局限性较大)
如果不想依赖链接器选项,你可能会尝试用宏重写调用逻辑,但这里有个坑:
你之前试的#define *some_func some_func不行,因为宏名不能包含星号。换个写法:
#define some_func (*some_func_real) extern void some_func_real();
这样库中的调用(some_func)()会展开成((*some_func_real))(),等价于直接调用some_func_real(),是正常工作的。但库中的声明extern void (*some_func)();会变成extern void (*(*some_func_real))();——这是声明一个指向函数指针的指针,会直接编译报错,所以这个方法只适用于能修改库声明的场景,对你来说不太适用。
最稳妥的替代方案:const修饰函数指针
如果上面的方法都有约束,用const修饰函数指针是最省心的选择:
- 在
my_file.h中修改指针声明为const:
extern void (*const some_func)();
- 在你的代码中初始化这个指针:
void my_some_func() { /* ... */ } void (*const some_func)() = my_some_func;
这样任何人试图修改some_func都会触发编译器错误,而且因为指针是只读的,编译器会自动把(some_func)()优化成直接调用my_some_func(),性能和直接调用函数几乎没有差别,还不用折腾链接器或宏。
内容的提问来源于stack exchange,提问作者arye

