【问题标题】:function prototype with void* parameter带 void* 参数的函数原型
【发布时间】:2013-12-29 21:15:56
【问题描述】:

我有两个函数,每个函数都有一个指向不同类型的指针:

void processA(A *);
void processB(B *);

是否有一个函数指针类型能够在不强制转换的情况下保存指向任一函数的指针? 我尝试使用

typedef void(*processor_t)(void*);
processor_t Ps[] = {processA, processB};

但它不起作用(编译器抱怨指针初始化不兼容)。

编辑:另一部分代码将在不知道类型的情况下遍历 Ps 的条目。此代码将传递一个 char* 作为参数。像这样:

Ps[i](data_pointers[j]);

编辑:谢谢大家。最后,我可能会使用这样的东西:

void processA(void*);
void processB(void*);

typedef void(*processor_t)(void*);
processor_t Ps[] = {processA, processB};

...

void processA(void *arg)
{
  A *data = arg;
  ...
}

【问题讨论】:

  • 没有参数列表,如果这是纯 C,它将编译
  • 如果允许,那么我很容易将A* 传递给processB。毕竟任何指针类型都可以转换为void*,因此它会让我违反类型安全。
  • 从技术上讲,它编译,它只是抛出一个警告。我的问题是是否可以在不强制转换初始化程序的情况下摆脱警告?

标签: c void-pointers function-prototypes


【解决方案1】:

如果你typedef void (*processor_t)();,那么这将在 C 中编译。这是因为空参数列表未指定函数参数的数量和类型,所以这个 typedef 只定义了一个类型,它是“指向返回 void 的函数的指针,采用未指定数量的未指定类型的参数。”

编辑:顺便说一句,您不需要在初始化列表中的函数名称前面加上与号。在 C 中,该上下文中的函数名称衰减为指向该函数的指针。

【讨论】:

  • 我想知道,省略号会达到同样的效果吗? (使其编译没有警告)
  • 刚试过,得到test1.c:4:29: error: ISO C requires a named argument before ‘...’
  • 嗯,这就回答了。我想我应该猜到了 :) 谢谢。
  • C 标准可能没有定义这种行为。 processA 是一个函数,它采用指向 A 的指针。 processor_t 指向的类型是一个没有参数列表的函数。将指向第一种类型函数的指针转换为指向第二种类型的指针是合法的。但是,如果使用第二种类型调用函数并使用 void * 类型的参数,则 void * 参数与所需的 A * 参数的类型不同。它们不兼容,正如 C 标准所定义的那样,并且行为未定义。
  • @Andrey:你是对的,我使用了错误的术语。然而,这点是正确的:如果你调用processA,它是一个函数,它接受一个指向A 的指针,使用一个指向函数的指针,该函数具有未指定的参数列表和一个指向@987654331 的指针@,行为未定义。
【解决方案2】:

如果你施放它们就可以了

processor_t Ps[] = {(processor_t)processA, (processor_t)processB};

顺便说一句,如果您的代码中充斥着这种类型的东西,并且到处都是switch 以确定您需要调用哪个函数,您可能想看看面向对象编程。我个人不太喜欢它(尤其是 C++...),但它确实可以很好地使用虚拟继承删除这种代码。

【讨论】:

    【解决方案3】:

    这可以通过使用联合来完成,而无需强制转换:

    typedef struct A A;
    typedef struct B B;
    
    void processA(A *);
    void processB(B *);
    
    typedef union { void (*A)(A *); void (*B)(B *); } U;
    
    U Ps[] = { {.A = processA}, {.B = processB} };
    
    int main(void)
    {
        Ps[0].A(0); // 0 used for example; normally you would supply a pointer to an A.
        Ps[1].B(0); // 0 used for example; normally you would supply a pointer to a B.
        return 0;
    }
    

    必须使用正确的成员名称调用函数;此方法仅允许您在每个数组元素中存储一个或另一个指针,而不是执行奇怪的函数别名。


    另一种选择是使用代理函数,该函数在使用指向char 的指针的参数进行调用时确实具有所需的类型,并以正确的类型调用实际函数:

    typedef struct A A;
    typedef struct B B;
    
    void processA(A *);
    void processB(B *);
    
    typedef void (*processor_t)();
    
    void processAproxy(char *A) { processA(A); }
    void processBproxy(char *B) { processB(B); }
    
    processor_t Ps[] = { processAproxy, processBproxy };
    
    int main(void)
    {
        char *a = (char *) address of some A object;
        char *b = (char *) address of some B object;
        Ps[0](a);
        Ps[1](b);
        return 0;
    }
    

    我在上面使用了char *,因为你说你正在使用它,但我通常更喜欢void *

    【讨论】:

    • 在我的例子中,有一个遍历数组 Ps 的代码。
    • @cvoque:说有代码遍历数组并没有传达有关如何使用数组元素的任何信息。代码可以包含 switch 语句或其他调度。
    • @cvoque:你打算如何调用这些函数?大概你有一些指针要传递给它们。那个指针是什么类型的?例如,您是否通过传递从原始A *B * 转换而来的void * 来调用每个函数,而不管其类型如何?或者您是否将实际的 A *B * 值传递给它们?
    • 我的意思是,在我的例子中,有一个通用代码遍历数组 Ps 的条目,而不知道每个函数到底是什么类型。它总是传递一个 char*。
    • @cvoque:在 C 2011 (N1570) 6.2.7 中,定义了兼容类型。大多数情况下,它们需要相同。 void *char *A * 不兼容。 (它们不可能是因为某些 C 实现为char 指针保留了比指向更大对象的指针更多的信息;不同类型的指针在机器中可能有不同的表示。)然后我们必须考虑是否使用一种类型的表达式调用函数当函数用另一种类型的表达式声明时是允许的。...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-15
    • 1970-01-01
    • 2019-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多