【问题标题】:How do I pass a function AND a variable list of arguments to another function in C?如何将函数和变量参数列表传递给 C 中的另一个函数?
【发布时间】:2016-07-07 18:38:41
【问题描述】:

我想将任意函数及其参数传递给 C 中的另一个函数。

例如像下面的代码(显然不起作用)

#include <stdio.h>

void doit(int (*f)(...), char *fname, ...)
{
    va_list argptr;
    va_start(argptr, fname);
    f(argptr)
    va_end(argptr);
}   

int func1(char *a, int b)
{
    fprintf(stderr, "func1 %s %d\n", a, b);
}   

int func2(char *a, int b, int c)
{
    fprintf(stderr, "func2 %s %d %d\n", a, b, c);
}

int main(int argc, char **argv)
{

    doit(func1, "func1", "blah", 10);

    return 0;
}   

【问题讨论】:

  • 我了解基本函数指针的工作原理。我的问题是看看你是否可以将变量参数传递给函数。这在理论上是可能的,因为在一天结束时,参数只是在堆栈上传递。我可以很容易地在汇编器中做到这一点,想知道是否有一个 C 技巧可以做到这一点而不诉诸汇编
  • C 标准不强制要求堆栈。典型的 ABI 不仅仅使用堆栈来传递参数。

标签: c function pointers syntax


【解决方案1】:

如果你想让他们参与这样的计划,你需要 va_list 转发器到你的函数。比如:

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

void doit(int (*f)(va_list va), char *fname, ...)
{
    va_list argptr;
    va_start(argptr, fname);
    f(argptr);
    va_end(argptr);
}

int func1(const char *a, int b)
{
    fprintf(stderr, "func1 %s %d\n", a, b);
    return 0;
}

int func1_va(va_list va)
{
    const char * a = va_arg(va, const char*);
    int b = va_arg(va, int);
    return func1(a,b);
}

int func2(const char *a, int b, int c)
{
    fprintf(stderr, "func2 %s %d %d\n", a, b, c);
    return 0;
}

int func2_va(va_list va)
{
    const char *a = va_arg(va, const char*);
    int b = va_arg(va, int);
    int c = va_arg(va, int);
    return func2(a,b,c);
}

int main(int argc, char **argv)
{

    doit(func1_va, "func1", "blah", 10);
    doit(func2_va, "func2", "blahblah", 100, 200);

    return 0;
}

输出

func1 blah 10
func2 blahblah 100 200

【讨论】:

  • 谢谢!正是我想要的。
  • @WhozCraig 可变参数的第一个元素("func1""func2")被删除,这是故意的吗?
  • @deamentiaemundi 他们没有被删除,也不是 va_list 的一部分。它们是doitfname 参数的正式参数,并且根本没有使用(OP 选择这样做,我只是效仿)。他们甚至需要在那里吗?并不真地。我们可以从f 轻松基于va_start
  • @WhozCraig,包装函数 func1_va 和 func2_va 不能通用吗?也许如果他们接受一个 char *fmt (就像 printf 那样)并解析格式并将参数传递给他们正在调用的函数?
  • 这就是我问的原因(我从不放弃第一个元素,我不想浪费它。但我在更艰难的时期长大,那时记忆不仅稀疏而且非常昂贵)
【解决方案2】:

首先:如果您不知道参数如何传递或激活记录如何工作,请不要乱用 va_args

最好的办法是用过多的参数声明 doit():

void doit(void (*p_func)(), int arg1, int arg2, int arg3, int arg4, int arg5);

然后使用所有这些参数调用 ( *p_func )() 。如果被调用的函数没有超出它的需要,就不会有任何麻烦。即使这样做,它也只会将垃圾值从堆栈中拉出。

【讨论】:

  • 这是ABI 特定的。在 x86-64 上它可能会工作,但不能保证其他架构。
猜你喜欢
  • 2022-09-30
  • 1970-01-01
  • 1970-01-01
  • 2012-02-19
  • 1970-01-01
  • 2019-10-09
  • 2013-12-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多