【问题标题】:Typecasting for functions with different arguments in cc中具有不同参数的函数的类型转换
【发布时间】:2018-04-10 04:53:13
【问题描述】:

所以我有两个函数做同样的事情,但在不同类型的变量上。

当给定int arr[] 参数时,第一个函数填充整数数组。 当给定结构作为参数时,第二个函数也用整数填充链表。 链表参数的结构看起来像这样

typedef struct {node_t *head;int size;} 
list_t;       

现在我已经为这两个函数实现了一个函数指针表:

typedef struct{
    char *name;  //name of the function
    void (*fill)(int arr[]); //fill up the array 
} alg_t;

alg_t algs[] = {
    {"func1",fill_up_arr},
    {"func2",fill_up_linkedList}
};

请注意,在保存我的指针的结构内部,填充函数指针 将int arr[] 作为参数。

我只想要该结构中的一个函数指针,有什么方法可以使用 类型转换,以便 fill_up_linkedList 等其他函数需要参数类型为 list_t 而不是 int arr[]

//This is what I want my main to look like.
//I want func.fill to be called only once thus
//dynamically perform the operations for all functions inside the table of 
//functions array
int arr = malloc(sizeof(int)algs.size);
for(int i = 0; i<algs.size;i++){
     alg_t func = algs[i];  
     func.fill(arr);
}                  

似乎这段代码的问题在于循环会尝试执行fill_up_LinkedList 函数,因为它需要不同的参数。 在这种情况下如何使用类型转换?

谢谢

【问题讨论】:

  • funcs_t algs[]?你的意思是alg_t algs[] 对吗?
  • 是的,谢谢。刚刚编辑过
  • 如果您使用相同的输入 (arr) 调用列表中的所有函数,那么所有函数都将具有相同的参数类型,所以这里没有问题。
  • 会有一个问题,因为 fillUp_linked_list 采用不同的参数。它将寻找一个结构,但它会找到一个 int arr[]
  • "我只想要该结构中的一个函数指针," 你的意思是要删除 name 只留下函数指针吗?如果你想调用两个单独的函数,你将需要 2 指针,除非你用 union 做一些事情,就像下面的 @Matthias 建议的那样。 (那么你必须手动控制哪个是最后分配的指针)

标签: c casting function-pointers typedef


【解决方案1】:

好消息

C11 §6.3.2.3 Pointers ¶8(在一般主题下§6.3 Conversions)说:

指向一种类型的函数的指针可以转换为指向另一种类型的函数的指针 键入并再次返回;结果应与原始指针比较。如果一个转换 指针用于调用类型与引用类型不兼容的函数, 行为未定义。

这意味着您可以将任何和所有函数指针存储在通用函数指针类型中,例如typedef void (*GenericFunctionPointer)(void)。然而,重要的是,在通过指针调用函数之前,将通用函数指针类型转换回正确的函数指针类型(并且提供正确的参数列表,并适当地处理返回类型,尽管忽略返回值(如果有的话)始终是一种选择)。

坏消息

对于两种不同的函数指针类型,每种类型都有一个函数实例,支持这一点所需的基础设施可能比节省的成本(如果有的话)更复杂。另一方面,如果您有两种或多种不同的函数指针类型,并且大多数类型(如果不是所有类型)都有许多代表性函数('many' 表示'多于一个',如计算机工程师的计数:“零,一个,很多”),那么基础设施就可以得到回报。问题之一是编组函数参数 - 如何使参数可访问,以便可以通过具有正确参数的指针调用函数。

因此,以这种方式做事既复杂又冗长。

规定的要求

comment 中,OP Moi 说:

我只想在结构中放置一个函数。我的目标是找到一种方法让fillArray 允许传递不同的参数。

我对使用未计数的数组作为参数列表有很大的保留,如问题所示 - void (*fill)(int arr[]) 已显示。在我看来,它应该是void (*fill)(size_t n, int arr[n]),使用可变长度数组表示法。如果您愿意,可以省略下标中的 nvoid (*fill)(size_t n, int arr[]) — 或者甚至使用 void (*fill)(int arr[], size_t n),这是更经典的参数顺序。

抛开这个顾虑,如果您希望单个函数接受不同的参数,实现此目的的一种方法是使用 void * 作为类型,但您必须注意问题——其中之一是类型安全。您还需要借鉴标准 C 函数 qsort()bsearch() 的想法。参数列表将包括指向数组开头的指针void *、数组中每个元素的大小以及数组中元素的数量。您可能还需要比较器功能的类似物。

不过,在单个被调用函数的内部,您可能最终会得到两个代码路径,因此尽管您通过指针调用单个函数,但最终会执行相当于实现两个函数的操作。您可以使用类似于qsort() 的接口,使两个函数具有相同的接口和不同的主体,并且您在alg_t 数组中使用两个指针。

总结

  • 您可能无法完全达到规定的要求。
  • 您可能需要两个逻辑上独立的函数来处理这两个独立的接口,即使您将所有代码都压缩在一个函数中。

【讨论】:

    【解决方案2】:

    使用联合:

    typedef struct {
        char *name;  //name of the function
        union {
            void(*fillArray)(int arr[]); //fill up the array 
            void(*fillList)(YourListType list); //fill up the list
        };
    } alg_t;
    
    alg_t a;
    a.fillArray(...);
    a.fillList(...);
    

    或者:

    typedef struct {
        char *name;  //name of the function
        union {
            void(*fillArray)(int arr[]); //fill up the array 
            void(*fillList)(YourListType list); //fill up the list
        } functions;
    } alg_t;
    
    alg_t a;
    a.functions.fillArray(...);
    a.functions.fillList(...);
    

    【讨论】:

    【解决方案3】:

    由于你只有两种类型,你可以只使用枚举和宏

    enum e_type {e_foo,e_bar};
    #define do_foobar(e,...) (e)?bar(__VA_ARGS__):foo(__VA_ARGS__)
    

    用你的函数名替换 foo 和 bar

    【讨论】:

      【解决方案4】:

      如果您只有函数参数而不是返回值的问题,则存在与不完整类型定义相关的技巧。诀窍在于将指针类型声明中的参数列表留空,如下所示:

       typedef void (*callback_ptr)();
      

      不同于:

       typedef void (*callback_ptr)(void);
      

      在第一种情况下,编译器不会检查传递给函数的参数,因为指针是指向未完全定义的函数类型的指针,而第二种情况明确表示该函数不需要参数并且会给你一个如果您尝试与他们一起调用它,则会出错。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-01-23
        • 2012-06-05
        • 2016-11-05
        相关资源
        最近更新 更多