运行时c函数指针传递参数

运行时c函数指针传递参数,c,function,pointers,dynamic,stack,C,Function,Pointers,Dynamic,Stack,我有一个关于c函数指针和向其传递参数的相当复杂的问题 我在查找表中有一个函数指针和两个函数地址。我通过串行接口获取所有数据。首先是必须调用的函数的编号。我在表中查找它,并将引用传递给我的函数指针 之后,我收到几对4字节值作为参数的数据。问题是,我必须用相同的返回类型调用不同的函数,但参数数量不同 是否有一种方法可以动态地将数据传递给函数调用。也许通过手动将它们推到堆栈上?找不到解决方案 有人有任何想法或提示来解决这个问题吗?有时,使用联合体的以下方法很有用: union foo { str

我有一个关于c函数指针和向其传递参数的相当复杂的问题

我在查找表中有一个函数指针和两个函数地址。我通过串行接口获取所有数据。首先是必须调用的函数的编号。我在表中查找它,并将引用传递给我的函数指针

之后,我收到几对4字节值作为参数的数据。问题是,我必须用相同的返回类型调用不同的函数,但参数数量不同

是否有一种方法可以动态地将数据传递给函数调用。也许通过手动将它们推到堆栈上?找不到解决方案


有人有任何想法或提示来解决这个问题吗?

有时,使用联合体的以下方法很有用:

union foo {
   struct {
      int arg1;
   } f1_args;
   struct {
      int arg1, arg2;
   } f2_args;
};
int f1(union foo*);    
int f2(union foo*);
int (*table[])(union foo*) = {f1, f2};
//...
union foo data;
//...
int answer = table[1](&data); // calls f2, which uses arg1 and arg2
而且,如果您愿意,
f1
f2
可以是“实”函数的简单包装,如:

int f1(union foo *u) { return f1_real(u->f1_args.arg1); }
int f2(union foo *u) { return f2_real(u->f2_args.arg1, u->f2_args.arg2); }
这是相当灵活的。但如果您的参数总是只有4字节整数,那么您可以去掉
联合
,只使用数组。重写后,上述内容变为:

int f1(uint32_t *a) { return f1_real(a[0]); }       // wrapper
int f2(uint32_t *a) { return f2_real(a[0], a[1]); } // wrapper
int (*table[])(uint32_t *) = {f1, f2};              // lookup table
//...
uint32_t data[99];                                  // data from e.g. serial port
//...
int answer = table[1](data);                        // calls f2, which uses two args

由于函数可以区分它们的参数,因此您可以始终为它们指定
类型。例如:

int f(...)
{
    /* extract one int */
}

int g(...)
{
    /* extract two floats */
}

...

int (*fp)(...);

if (type_one)
    fp(10);
else if (type_two)
    fp(1.3, 4.3);
或者最好使用
联合体
。但是,在您的特定情况下,由于参数本身是“4字节对”,因此始终可以使用数组:

struct arg
{
    uint32_t pair_of_4_bytes[2];
};

int f(struct arg *args, size_t count)
{
}

int g(struct arg *args, size_t count)
{
}

...

int (*fp)(struct arg *args, size_t count);
struct arg args[MAX];
size_t count = 0;

/* get args from serial and put in args/count */
fp(args, count);

我不相信有一个简单的方法来回答这个问题,因为参数传递是特定于ABI(应用程序二进制接口)的。如果您的平台是固定的,并且您不介意编写不可移植的代码,那么您可以用ABI特定的方式编写代码,但我不建议这样做

我在自己编写的一个小型仿真器中解决了这个问题,对我来说,这更容易,因为命令的数量从来都不超过4个,我基本上使用了一个函数指针的并集,它有我想要的参数数量和一个switch语句来选择正确的参数

typedef struct
{
    union
    __attribute__((__packed__))
    {
        void      *func;
        void     (*func_a0)(void);
        uint32_t (*func_a0r)(void);
        void     (*func_a1)(uint32_t);
        uint32_t (*func_a1r)(uint32_t);
        void     (*func_a2)(uint32_t, uint32_t);
        uint32_t (*func_a2r)(uint32_t, uint32_t);
        void     (*func_a3)(uint32_t, uint32_t, uint32_t);
        uint32_t (*func_a3r)(uint32_t, uint32_t, uint32_t);
        void     (*func_a4)(uint32_t, uint32_t, uint32_t, uint32_t);
        uint32_t (*func_a4r)(uint32_t, uint32_t, uint32_t, uint32_t);
    };
    unsigned    args;
    bool        ret;
    const char* name;
} jump_entry_t;

bool jump_table_exec(
    jump_table_t* table, void* addr,
    uint32_t* args, uint32_t* ret)
{
    #ifdef JUMP_TABLE_DEBUG
    if (!table)
        return false;
    #endif

    if ((uintptr_t)addr < (uintptr_t)table->base)
        return false;
    unsigned i = ((uintptr_t)addr - (uintptr_t)table->base);
    if ((i & 4) || (i >= table->size))
        return false;

    jump_entry_t j = table->entry[i >> 3];
    if (!j.func)
        return false;
    if (j.args && !args)
        return false;

    if (j.ret)
    {
        if (!ret) return false;
        switch (j.args)
        {
            case 0:
                *ret = j.func_a0r();
                break;
            case 1:
                *ret = j.func_a1r(args[0]);
                break;
            case 2:
                *ret = j.func_a2r(args[0], args[1]);
                break;
            case 3:
                *ret = j.func_a3r(args[0], args[1], args[2]);
                break;
            case 4:
                *ret = j.func_a4r(args[0], args[1], args[2], args[3]);
                break;
            default:
                return false;
        }
    }
    else
    {
        switch (j.args)
        {
            case 0:
                j.func_a0();
                break;
            case 1:
                j.func_a1(args[0]);
                break;
            case 2:
                j.func_a2(args[0], args[1]);
                break;
            case 3:
                j.func_a3(args[0], args[1], args[2]);
                break;
            case 4:
                j.func_a4(args[0], args[1], args[2], args[3]);
                break;
            default:
                return false;
        }
    }

    #ifdef JUMP_TABLE_DEBUG
    if (j.name)
    {
        fprintf(stderr, "Info: Jump table %s(", j.name);
        if (j.args >= 1) fprintf(stderr,   "%" PRIu32, args[0]);
        if (j.args >= 2) fprintf(stderr, ", %" PRIu32, args[1]);
        if (j.args >= 3) fprintf(stderr, ", %" PRIu32, args[2]);
        if (j.args >= 4) fprintf(stderr, ", %" PRIu32, args[3]);
        fprintf(stderr, ")");
        if (j.ret) fprintf(stderr, " returned %" PRIu32, *ret);
        fprintf(stderr, ".\n");
    }
    #endif
    return true;
}
typedef结构
{
联盟
__属性(压缩的)
{
void*func;
无效(*func_a0)(无效);
uint32_t(*func_a0r)(无效);
无效(*func_a1)(uint32_t);
uint32_t(*功能)(uint32_t);
无效(*func_a2)(uint32_t,uint32_t);
uint32_t(*func_a2r)(uint32_t,uint32_t);
无效(*func_a3)(uint32_t、uint32_t、uint32_t);
uint32_t(*功能a3r)(uint32_t、uint32_t、uint32_t);
无效(*func_a4)(uint32_t、uint32_t、uint32_t、uint32_t);
uint32_t(*功能a4r)(uint32_t、uint32_t、uint32_t、uint32_t);
};
无符号args;
布尔-雷特;
常量字符*名称;
}跳转进入;
bool jump_table_exec(
跳转表格表格,无效*地址,
uint32_t*参数,uint32_t*ret)
{
#ifdef跳转表调试
如果(!表)
返回false;
#恩迪夫
if((uintpttr\u t)addr<(uintpttr\u t)表->基)
返回false;
无符号i=((uintptr\u t)addr-(uintptr\u t)table->base);
如果((i&4)| |(i>=表格->大小))
返回false;
跳转条目j=表格->条目[i>>3];
if(!j.func)
返回false;
if(j.args&&!args)
返回false;
如果(j.ret)
{
如果(!ret)返回false;
开关(j.args)
{
案例0:
*ret=j.func_a0r();
打破
案例1:
*ret=j.func_a1r(args[0]);
打破
案例2:
*ret=j.func_a2r(args[0],args[1]);
打破
案例3:
*ret=j.func_a3r(args[0],args[1],args[2]);
打破
案例4:
*ret=j.func_a4r(args[0],args[1],args[2],args[3]);
打破
违约:
返回false;
}
}
其他的
{
开关(j.args)
{
案例0:
j、 func_a0();
打破
案例1:
j、 func_a1(args[0]);
打破
案例2:
j、 func_a2(args[0],args[1]);
打破
案例3:
j、 func_a3(args[0],args[1],args[2]);
打破
案例4:
j、 func_a4(参数[0],参数[1],参数[2],参数[3]);
打破
违约:
返回false;
}
}
#ifdef跳转表调试
如果(j.姓名)
{
fprintf(stderr,“信息:跳转表%s(“,j.name”);
如果(j.args>=1)fprintf(stderr,“%”PRIu32,args[0]);
如果(j.args>=2)fprintf(stderr,,%“PRIu32,args[1]);
如果(j.args>=3)fprintf(stderr,,%“PRIu32,args[2]);
如果(j.args>=4)fprintf(stderr,,%“PRIu32,args[3]);
fprintf(标准字符“)”;
如果(j.ret)fprintf(stderr,“returned%”前32,*ret);
fprintf(标准格式,“.\n”);
}
#恩迪夫
返回true;
}
我认为“可变函数”可以解决您的问题。这里有一个简单的例子:


也许展示一些代码会有帮助,以及您到目前为止所做的尝试?您研究过“可变函数”吗?您不能在C中动态生成函数调用。要么使用一个大开关,根据函数为您执行正确的调用,要么您必须转到汇编程序来执行调用(它们并不总是被推到堆栈上,您必须找出调用约定)。可能有可用的编译器扩展,但我从来没有听说过有助于实现这一点的扩展。基本问题是在编译时是否知道所有可能的参数类型排列。您至少需要一个命名参数,所以
int f(…)
不起作用。