在C语言中指向不同参数函数的函数指针

20

我有两个函数,参数数量和类型都不固定

double my_func_one(double x, double a, double b, double c) { return x + a + b + c }
double my_func_two(double x, double p[], double c) { return x + p[0] + p[1] + c }

我想根据某个条件变为真时,使用一个指向我上面定义的函数的指针,例如:

if (condition_1)
   pfunc = my_func_one;
else if (condition_2)
   pfunc = my_func_two;

 // The function that will use the function I passed to it
 swap_function(a, b, pfunc);

我的问题是,对于这种情况,我是否可以定义一个函数指针?如果可以的话,该怎么做?
我理解的是,函数指针的原型应该对所有它能指向的函数都是相同的。

typedef double (*pfunction)(int, int);

在我的情况下,它们不是相同的。是否有其他方法来做到这一点?

语言

我正在使用gcc 4.4.3编译器/链接器开发C语言。


3
将函数指针变为void *,然后在调用时进行适当的类型转换?但这会失去所有类型安全性。 - Some programmer dude
6个回答

32

最干净的方法是使用union:

typedef union {
  double (*func_one)(double x, double a, double b, double c);
  double (*func_two)(double x, double p[], double c);
} func_one_two;

接下来可以初始化一个union的实例,并向swap_function函数提供信息,以表示哪个字段是有效的:

func_one_two func;

if (condition_1)
   func.func_one = my_func_one;
else if (condition_2)
   func.func_two = my_func_two;

 // The function that will use the function I passed to it
 swap_function(a, b, func, condition_1);

假设 swap_function 可以根据 condition_1false 推断出它应该假定 condition_2。请注意,联合体是按值传递的;它只有一个函数指针大小,所以传递它并不比传递指针更昂贵。


13

我的问题是,在这种情况下,我是否可以定义一个函数指针?

不行(除非通过肮脏的类型转换)。

还有其他方式吗?

最好的办法是为您现有的其中一个函数创建一个包装函数。例如:

double my_func_one_wrapper(double x, double p[], double c) {
    return my_func_one(x, p[0], p[1], c);
}

这样,您就拥有了具有相同签名的两个函数,因此它们具有相同的函数指针类型。


2

虽然这是一个老话题,但我也遇到了同样的问题,最终想出了这个解决方案。

如果你想为每个函数使用相同的原型,你可以像这样将参数包装在结构中:

 typedef struct {
     double a,
            b,
            c; 
 }chunk1_t;

  typedef struct {
     double p[];
     double c; 
 }chunk2_t;

然后你的函数指针将变为:
 double (*pfunc) (double x, void *args);

这将导致类似于以下内容:
 pfunc cb;

 double swap_function(double x, pfunc cb, void *args);

 double my_func_one(double x, void *args) { 
   chunk1_t *chunk = (chunk1_t*) args;
   return x + chunk->a + chunk->b + chunk->c; 
 }

 double my_func_two(double x, void *args) {
   chunk2_t *chunk = (chunk2_t*) args;
   return x + chunk->p[0] + chunk->p[1] + chunk->c ;
 }

 int main(){
   // declare a, b,...
   double a = 1.0f;
   //...
   // cast for safety
   chunk1_t myChunk1 = {(double)a, (double)b, (double)c};
   // don't if you like risk
   chunk2_t myChunk2 = {p, c};

   swap_function(x, cb, &myChunk1); 
   swap_function(x, cb, &myChunk2);
 }

使用存储在结构体中的函数指针:

 #define FUNC_ONE_METHOD 1
 #define FUNC_TWO_METHOD 2

 typedef struct chunk{
     double a, b, c;
     double p[];
     int method;
     double (*pfunc) (double x, struct chunk *self);
 }chunk_t;

 double swap_function(double x, chunk_t *ch){
    switch (ch->method)
    {
        case FUNC_TWO_METHOD:
            ch->pfunc = my_func_two;
            break;
        case FUNC_ONE_METHOD:
            ch->pfunc = my_func_one;
            break;
        default:
            return -1; // or throw error
    return ch->pfunc(x, ch);
 }


 chunk c = {.a= 1, .b=3, .c=1, .method=1};
 swap_function(x, &c);

1
你的理解是正确的。
你的函数指针的签名必须与相应的函数匹配。
考虑learncpp.com

就像可以声明一个指向变量的非常量指针一样,也可以声明一个指向函数的非常量指针。这样做的语法是最丑陋的事情之一:

// pFoo is a pointer to a function that takes no arguments and returns an integer 
int (*pFoo) (); 

在*pFoo周围的括号是必要的,因为int *pFoo()会被解释为一个名为pFoo的函数,它不带参数并返回一个指向整数的指针,这是优先级原因。在上面的代码片段中,pFoo是一个没有参数并返回整数的函数指针。pFoo可以“指向”任何符合此签名的函数。请注意,函数指针的签名(参数和返回值)必须与函数的签名匹配。

实际上,pFoo 是一个指向可以接受任意类型和数量参数的函数的指针。请参见 https://godbolt.org/z/Y8M3jP。 - Lars M.

1
你想要的是可能的,但有点不太好。函数指针可以相互转换而不会丢失信息。重要的是,你必须始终通过这样的指针调用一个与其定义相对应的签名函数。所以如果在调用函数之前进行类型转换并使用正确的参数进行调用,一切都应该没问题。

-2

类型转换方法的示例,用于使用相同的函数指针来调用不同原型的函数。 <>

#include <stdio.h>
typedef void (*myFuncPtrType) (void);

typedef int (*myFunc2PtrType)(int, int);

typedef int * (*myFunc3PtrType)(int *);

static void myFunc_1 (void);
static int myFunc_2 (int, int);
static int* myFunc_3 (int *);

const myFuncPtrType myFuncPtrA[] = {
                                    (myFuncPtrType)myFunc_1,
                                    (myFuncPtrType)myFunc_2,
                                    (myFuncPtrType)myFunc_3
};

static void myFunc_1 (void)
{
    printf("I am in myFunc_1 \n");
}

static int myFunc_2 (int a, int b)
{
    printf("I am in myFunc_2\n");
    return (a+b);
}

static int* myFunc_3 (int *ptr)
{
    printf("I am in myFunc_3\n");
    *ptr = ((*ptr) * 2);
    return (ptr+1);
}

int main(void) {
    // your code goes here
    int A[2],C;

    int* PTR = A;

    (*(myFuncPtrA[0]))();

    A[0]=5;
    A[1]=6;

    C = ((myFunc2PtrType)(*(myFuncPtrA[1])))(A[0],A[1]);

    printf("Value of C: %d \n", C);

    printf("Value of PTR before myFunc_3: %p \n", PTR);
    printf("Value of *PTR before myFunc_3: %d \n", *PTR);

    PTR = ((myFunc3PtrType)(*(myFuncPtrA[2])))(&A);

    //Lets look how PTR has changed after the myFunc_3 call

    printf("Value of PTR after myFunc_3: %p \n", PTR);
    printf("Value of *PTR after myFunc_3: %d \n", *PTR);


    return 0;
}

1
你能否添加一份描述,说明你所做的事情?而不仅仅是粘贴代码。 - kaldoran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接