【问题标题】:How to retrieve a function from a function argument in C?如何从 C 中的函数参数中检索函数?
【发布时间】:2017-04-01 12:08:48
【问题描述】:

我一直在从函数参数中获取函数指针。 这是我正在尝试做的一个示例。

int a(int I)
{
    return I*I;
}

void get_a(int(*R)(int))
{
    R = &a;
}

int main()
{
    int(*function)(int) = NULL;

    printf("function a=%p\n", a);

    get_a(function);
    printf("function a=%p\n", &function); // Is never the right address.

    return 0;
}

网上搜了也没用,只能找到传函数作为参数的例子,所以放弃尝试自己解决这个问题。

【问题讨论】:

  • 这与“如何从函数参数中检索 int”没有什么不同。先解决这个问题,你也会明白这个问题的。
  • 你的函数 get_a() 什么都不做,因为它所做的只是分配给一个局部变量,然后什么也不返回。一个好的编译器甚至可以完全优化它。

标签: c function pointers arguments


【解决方案1】:

这里有两个问题。

首先,您的函数正在修改一个局部变量,因此结果将永远不会反映在调用者中。您需要传递函数指针变量的地址,然后让get_a 接受指向函数指针的指针。通过传递变量的地址,您可以取消引用并修改它。

第二个问题是您的第二个printf 正在打印function地址,而不是它的。传入值。

void get_a(int(**R)(int))
{
    *R = &a;
}

int main()
{
    int(*function)(int) = NULL;

    printf("function a=%p\n", (void *)a);

    get_a(&function);
    printf("function a=%p\n", (void *)function);

    return 0;
}

结果:

function a=0x400498
function a=0x400498

还要注意void * 的演员表。传递指向 printf 的指针以通过 %p 打印是需要强制转换为 void * 的罕见情况之一,否则您可能会遇到未定义的行为。

【讨论】:

  • 感谢您快速回复一个工作示例,这正是我所需要的:)。尝试了几种带有额外 * 和 & 的组合,但显然没有这个。
  • @PuzzlingProgrammer 很高兴我能帮上忙。如果您觉得有用,请随时 accept this answer
  • 这两种情况下都应该是printf("... %p ...", (void *)whatever),否则行为未定义。 (这不仅仅是因为我们正在处理指向函数的指针;printf 是可变参数,因此不会自动转换指针;必须将 %p 的参数转换为 void * 除非它的类型已经是[const] void * 或(仅在 C11 中)[const] char *。)
  • @zwol 很好。已编辑。
  • 将函数指针转换为 void 是严格实现中的 ub。
【解决方案2】:

第一个问题是你打印了一个指向function 的指针,而不是它的值;它永远不会给你正确的地址。

解决了这个问题,剩下的问题是R 是一个按值传递的函数指针。在get_a 中对其进行的任何更改都是函数本地的,并且不会反映在main 中的function 的值中。

你有两个解决这个问题的方法:

  • 使get_a返回函数指针,并在main中赋值function = get_a(),或者
  • 将指向函数指针的指针传递给get_a,并使用取消引用进行赋值,即使用与非函数指针相同的技术。

以下是您如何实施第二种方法:

void get_a(int(**R)(int)) {
    *R = a;
}
...
get_a(&function);

Demo.

注意: ISO C 禁止将函数指针转换为void*,这是使用%p 打印所必需的。不过,我假设您这样做是出于调试目的。

【讨论】:

  • 第一种方法的解决方案应该是int (*get_a())(int) {return &a;}。凌乱。
  • 好奇:ISO C 是否允许uintmax_t (or uintptr_t) p = (cast_to_type) function_ptr'?即使目标类型对于所有function_ptr 不够宽?
  • @chux 好问题,我不知道。
  • @chux 只有指向 void 的指针可以转换为 uintptr_t,反之亦然。
  • @2501 感谢您指出这一点。然而void*uintptr_t 的转换规范是保持平等。 (往返)。我的评论是想知道是否可以将指向任何函数的指针(不带 UB)转换为 uintptr_t - 不考虑其往返等效性。也许我稍后会对此进行更多研究。
猜你喜欢
  • 2012-01-17
  • 2013-06-27
  • 2016-06-02
  • 2011-08-08
  • 2017-07-23
  • 1970-01-01
  • 2012-05-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多