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