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

时间:2014-03-19 16:04:43

标签: c function pointers dynamic stack

我有一个关于c函数指针并将参数传递给它们的相当复杂的问题。

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

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

有没有办法将动态数据传递给函数调用。也许通过手动将它们推到堆栈上?无法找到解决方案。

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

4 个答案:

答案 0 :(得分:1)

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

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

而且,如果您愿意,f1f2可以是“真实”函数的简单包装,如:

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字节的整数,那么你就可以摆脱union而只使用数组。改写后,上面变为:

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

答案 1 :(得分:1)

我不相信这是一个简单的方法来解决这个问题,因为参数传递是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;
}

答案 2 :(得分:0)

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

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);

或者更好的是使用union。但是,在您的特定情况下,由于参数本身是&#34;对4个字节&#34;,您始终可以使用数组:

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);

答案 3 :(得分:0)

我认为&#34; Variadic功能&#34;可以解决您的问题需求。在这里查看一个简单的例子:

http://www.gnu.org/software/libc/manual/html_node/Variadic-Example.html#Variadic-Example