Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/jpa/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
给定一个带有原型的char*,我们可以将void*强制转换为函数指针吗?然后运行它?_C - Fatal编程技术网

给定一个带有原型的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++好建议,谢谢,你的回答对我最有用。