【问题标题】:c function pointer pass parameter during runtimec函数指针在运行时传递参数
【发布时间】:2014-04-26 00:57:03
【问题描述】:

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

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

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

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

有人有解决这个问题的想法或提示吗?

【问题讨论】:

  • 也许它会帮助显示一些代码,以及到目前为止你尝试了什么?
  • 你研究过“可变参数函数”吗?
  • 您不能在 C 中动态生成函数调用。要么有一个大开关,它会根据函数为你执行正确的调用,要么你必须放弃到汇编程序来执行调用(它们并不总是被压入堆栈,您必须找出调用约定)。可能有可用的编译器扩展,但我从未听说过可以帮助解决此问题的扩展。
  • 基本问题是在编译时是否知道所有可能的参数类型排列。

标签: c function pointers dynamic stack


【解决方案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

【讨论】:

    【解决方案2】:

    我认为“可变参数函数”可以解决您的问题需求。在此处查看一个简单的示例:

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

    【讨论】:

      【解决方案3】:

      我认为没有简单的方法来回答这个问题,因为参数传递是 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;
      }
      

      【讨论】:

        【解决方案4】:

        由于函数可以区分它们的参数,你总是可以给它们一个...类型。例如:

        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。但是,在您的特定情况下,由于参数本身是“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);
        

        【讨论】:

        • 您至少需要一个命名参数,所以int f(...) 将不起作用。
        猜你喜欢
        • 1970-01-01
        • 2010-09-05
        • 1970-01-01
        • 1970-01-01
        • 2012-05-03
        • 1970-01-01
        • 2010-09-29
        • 1970-01-01
        • 2022-01-20
        相关资源
        最近更新 更多