如何在C语言中将“类型”作为参数传递给函数?

12

我想编写一个通用函数(例如,接受以void**类型表示的数组作为参数并对该数组执行某些操作),使得该函数将元素的类型(在此示例中,将是数组中任何元素的类型)作为参数传递。

我可以在C语言中实现吗?


例如:

我想编写一个函数,接受以void**类型表示的数组作为参数,并以某种随机方式初始化该数组。

所谓“以某种随机方式”,是指一个函数作为参数接收:数组(以void**类型表示),数组中任何元素的类型,索引(以int类型表示),并初始化该单元格。


我想在c语言中编写一个通用函数,例如,编写一个函数,它可以获取数组(类型为void**),并对该数组执行某些操作。我想知道是否可以将该数组中任何元素的类型(例如)传递给函数。 - AskMath
宏可以(在某种程度上)实现这一点。C语言中没有像C++中的模板那样的东西。 - Support Ukraine
1
在某种程度上,您可以使用C11的_Generic编写通用函数,但您几乎必须事先知道您想要泛型化的所有类型。 - Antti Haapala -- Слава Україні
4个回答

16

只有在您拥有标准的C编译器的情况下才能实现此功能,这种情况下,您可以使用_Generic关键字来实现。您必须为每个支持的类型编写不同的函数。

#include <stdio.h>

#define func(x) _Generic((x), int: func_int, char: func_char)(x);

void func_int (int x)
{
  printf("%s\t%d\n", __func__, x);
}

void func_char (char x)
{
  printf("%s\t%c\n", __func__, x);
}


int main(void)
{
  int i = 5;
  char c = 'A';

  func(i);
  func(c);
}

输出:

func_int        5
func_char       A

2
你应该添加一两个参考链接(http://en.cppreference.com/w/c/language/generic);这只适用于 C11。此外,“标准的C编译器”基本上是没有意义的,除非你指定具体哪一个标准。 - anon
1
除非原帖明确提到另一个 C 版本,否则我们可以假设他们正在使用标准的 C。 - Lundin
2
@Lundin 再次问,是哪个标准?完全有可能阅读此答案的某人正在使用 C99 编译器——比如因为他们所在的学校还没有转向实际的、现代的编译器,这种情况令人不安——如果至少没有指定,那么这将浪费他们的时间。 - anon
@NicHartley ISO 9899。他们只有一个活跃的标准。请参阅https://stackoverflow.com/tags/c/info - Lundin
1
@Lundin 但是“活动标准”和“使用标准”_并不相同_。就像我说的,有人可能被限制在较旧版本的C上。你浪费了更多的时间来争论这个问题,而你本可以把“标准C”改为“C11”,这样更简单、明确,对于新手来说更容易理解。我不想再和你争论这个问题了;如果你不能想出让事情变得更容易阅读而又不需要额外付出努力的好处,那么我会让你自己去想出来的。 - anon
@NicHartley 你从中无法理解的是什么:“除非问题明确提到使用的C标准版本,否则假定使用当前版本。也就是说,ISO当前列出的任何ISO 9899版本都可以使用。回答或评论标记为[tag:c]的问题时,请记住这一点。” - Lundin

8
您没有传递“类型”。C语言无法在运行时编码和解码类型信息。操作对象的函数必须静态地知道类型。如果您坚持使用指向 void 的指针,您需要委托给一个了解类型信息的函数。这可以通过回调来实现。例如,标准库函数qsort接受一个用于比较对象值的回调函数:
void qsort( void *ptr, size_t count, size_t size,
            int (*comp)(const void *, const void *) );

调用代码提供回调函数,在回调函数内部,将会转换回需要比较的静态类型。这就是通常使用指向 void 的指针的方式,我们以抽象形式定义所需执行的操作集,然后要求调用代码提供这些操作的实现。


5
这里有一个宏技巧的例子。
func.h
#ifndef FUNC_H
#define FUNC_H

#define add(a, b, typename) functionAdd##typename(a,b)

/* function declarations */
#define declared(typename) \
typename functionAdd##typename(typename, typename)

declared(int);
declared(float);

#endif

func.c

#include "func.h"

/* function code */
#define functionAdd(a, b, typename) \
typename functionAdd##typename(typename a, typename b){ \
    return a+b; \
}

/* function bodies (definitions) */
functionAdd(a, b, int)
functionAdd(a, b, float)

main.c

#include <stdio.h>
#include "func.h"

int main()
{
    int x1 = add(1, 2, int);
    float x2 = add(3.0, 4.0, float);
    printf("%d %f\n", x1, x2);  
    return 0;
}

3

另一种解决方案可能是定义一个枚举来表示这样的类型:

#include "stdio.h"

typedef enum {
    TYPE_INT,
    TYPE_CHAR,
    TYPE_STRING
} type_id;

int print(type_id type, void *data) {
    switch (type) {
    case TYPE_INT:
        // Do something with data as int
        printf("%d\n", * (int *)data);
        break;
    case TYPE_CHAR:
        // Do something with data as char
        printf("%c\n", * (char *)data);
        break;
    case TYPE_STRING:
        // Do something with data as string
        printf("%s\n", (char *)data);
        break;
    }
}

int main() {
    int a = 5;
    char b = 'a';
    char *c = "string";

    print(TYPE_INT, &a);
    print(TYPE_CHAR, &b);
    print(TYPE_STRING, c);

    return 0;
}

我更喜欢 Lundin 的建议,因为它提供了类型安全。


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