给定一个带有原型的char*,我们可以将void*强制转换为函数指针吗?然后运行它?
我在一个驱动程序中声明了许多函数,并且在节点格式的列表中将指向函数的指针传递给另一个驱动程序:给定一个带有原型的char*,我们可以将void*强制转换为函数指针吗?然后运行它?,c,C,我在一个驱动程序中声明了许多函数,并且在节点格式的列表中将指向函数的指针传递给另一个驱动程序: struct node { char def_prototype[256]; //example:(int (*)(wchar, int, int)) void *def_function; }; 是否有方法将def_函数类型转换为def_prototype中给出的原型 目前我正在使用simple switch和strcmp,但如果可能的话,我想对其进行推广。 PS:我知道在void指针和函数指
struct node
{
char def_prototype[256]; //example:(int (*)(wchar, int, int))
void *def_function;
};
是否有方法将def_函数类型转换为def_prototype中给出的原型
目前我正在使用simple switch和strcmp,但如果可能的话,我想对其进行推广。PS:我知道在void指针和函数指针之间进行转换是不安全的(正如SO中的各个地方所提到的),但在危急时刻需要采取危急措施,我已经非常小心了 编辑:
很抱歉不够清晰。我想实际调用函数(不仅仅是强制转换),在运行时根据提供的char[]生成函数指针 再次编辑:
由于我在内核级别(windows驱动程序)工作,因此我无法访问太多资源,因此,我坚持使用当前的实现(通过一些更改来消除后门)。感谢大家的帮助。因为C没有运行时类型信息,所以绝对不需要像您考虑的那样执行动态强制转换。只要传递指针,如果一切都合适,它就会工作。如果指针没有指向具有正确签名的函数,则无法修复它。除非您想弄乱程序集的敲击声(在跳转之前将数据推到堆栈上,等等),否则有更好的方法,而不是执行一些切换情况
如果目标函数是有限且已知的,为什么不为其创建一个查找表(
map
)?基本上有两种解决方案:
if
-else
梯形图ISO-C不允许在函数和数据指针之间进行强制转换,即应该使用
void(*)(void)
而不是void*
来保存函数
除此之外,YeenFei的断言是正确的,即没有通用的平台无关解决方案,这意味着您在C本身所能做的最好的事情就是提供一个受支持的签名列表
您应该实现自己的编码方案,而不是使用普通的C原型。通常使用字符串,其中每个字符表示一个函数参数(第一个字符表示返回值);例如,类型为int(*)(wchar,int,int)
的函数可以具有签名“iwii”
然后可以使用bsearch()
和strcmp()
轻松构建签名查找表;下面是一个完整的示例:
#include <assert.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int cmp(const void *key, const void *element)
{
return strcmp(key, *(const char * const *)element);
}
static _Bool dispatch(const char *sig, void (*func)(void), void *retval, ...)
{
// supported signatures; must be ordered according to strcmp()
static const char * const SIGS[] = { "iii", "v", "vi" };
const char * const *match = bsearch(
sig, SIGS, sizeof SIGS / sizeof *SIGS, sizeof *SIGS, cmp);
if(!match) return 0;
va_list args;
va_start(args, retval);
switch(match - SIGS)
{
case 0: { // iii
int a1 = va_arg(args, int);
int a2 = va_arg(args, int);
int rv = ((int (*)(int, int))func)(a1, a2);
if(retval) memcpy(retval, &rv, sizeof rv);
break;
}
case 1: { // v
func();
break;
}
case 2: { // vi
int a1 = va_arg(args, int);
((void (*)(int))func)(a1);
break;
}
default:
assert(!"PANIC");
}
va_end(args);
return 1;
}
// example code:
static int add(int a, int b)
{
return a + b;
}
int main(void)
{
int sum;
dispatch("iii", (void (*)(void))add, &sum, 3, 4);
printf("%i", sum);
return 0;
}
#包括
#包括
#包括
#包括
#包括
静态int cmp(常量无效*键,常量无效*元素)
{
返回strcmp(key,*(const char*const*)元素);
}
静态布尔分派(常量字符*sig,void(*func)(void),void*retval,…)
{
//支持的签名;必须根据strcmp()进行排序
静态常量字符*常量SIGS[]={“iii”、“v”、“vi”};
常量字符*常量*匹配=b搜索(
sig、SIGS、sizeof SIGS/sizeof*SIGS、sizeof*SIGS、cmp);
如果(!match)返回0;
va_列表参数;
va_开始(args,retval);
开关(匹配-SIGS)
{
案例0:{//iii
int a1=va_arg(args,int);
int a2=va_arg(args,int);
int rv=((int(*)(int,int))func)(a1,a2);
if(retval)memcpy(retval,&rv,rv尺寸);
打破
}
案例1:{//v
func();
打破
}
案例2:{//vi
int a1=va_arg(args,int);
((无效(*)(内部))职能)(a1);
打破
}
违约:
断言(!“恐慌”);
}
va_端(args);
返回1;
}
//示例代码:
静态整数相加(整数a,整数b)
{
返回a+b;
}
内部主(空)
{
整数和;
派遣(“iii”、(无效(*)(无效))添加和汇总,3,4);
printf(“%i”,总和);
返回0;
}
类似想法的一个很好的实现是。这实现了使用任意调用约定和签名声明和调用函数的详细信息。它是(令人惊讶的)平台可移植的,并且已知可以在Linux和Windows上开箱即用
它的一个使用示例是Lua扩展库。这演示了如何调用在运行时声明的任意函数,以及如何从本机Lua类型调整为调用约定所需的类型。特定的Lua绑定对您没有用处,但它提供了一个完整的工作示例,说明了如何以及为什么实际使用libffi。我认为作者要求提供一种方法,根据字符串内容在运行时生成函数原型,然后调用给定的函数(地址)您是否知道def_原型数组中的意外溢出会覆盖指向函数的指针?这将允许邪恶博士炸毁你的密码并控制世界:-)@Bart,嘘。。。每个程序员都给自己留了一个后门:呸,谢谢巴特,我会很快想出一个解决方案。@巴特:对于这个世界来说,问题与其说是有意溢出,不如说是无意溢出,但在某个地方,你需要一些逻辑,将你可能的输入数据映射到那些函数实际需要的参数。一个varchar和两个int->使用A,x,y。两个瓦查尔->使用a,b。三个整数->使用x,y,z。等等,比我的解决方案好得多,但我在驱动程序级别工作,我认为map不可用。顺便说一句,这是c,不是c++好建议,谢谢,你的回答对我最有用。