3

我是 C 的新手。我想知道如果我有三个这样的数组:

int a[] = {1, 2, 3}
char b[] = {'a', 'c', 'k'}
float c[] = {4.5, 5.8}

是否可以编写一个多态函数来打印这些数组中的任何一个?

prarray(a); prarray(b); prarray(c);

这种形式也是可以接受的:

prarray(a, int); prarray(b, char); prarray(c, float)

是否可以解决该功能void prarray(void *)?有没有人有任何想法?

4

6 回答 6

6

最新的 C 标准是 C11 (C 2011)。

它提供了关键字_Generic(第 6.5.1.1 节通用选择,在第 6.5 节表达式下)来执行您要求的那种事情。

例如,

#define prarray(A, len) _Generic((A), \
    int: prarray_int, \
    char: prarray_char, \
    float: prarray_float, \
    )(A, len)

void prarray_int( int* a, ptrdiff_t len ) { ... }
void prarray_char( char* a, ptrdiff_t len ) { ... }
void prarray_float( float* a, ptrdiff_t len ) { ... }

现在您需要做的就是获得一个 C11 编译器!:-)

或者更手动地执行 C11 宏的工作 - 实际上,将类型名称作为显式宏参数传递,然后使用##标记粘贴来生成函数名称。

免责声明:未经测试的代码(我没有 C11 编译器),而且,自 1990 年代后期以来我没有使用过 C。

于 2012-10-06T00:26:09.477 回答
3

如果不对被调用函数提供大量帮助,您将无法做到这一点,以便它可以完成工作。查看标准 C 库中的两个“多态”函数,qsort()以及bsearch().

void qsort(void *base, size_t nel, size_t width,
       int (*compar)(const void *, const void *));
void *bsearch(const void *key, const void *base, size_t nel,
       size_t width, int (*compar)(const void *, const void *));

printf()scanf()系列是处理多种类型的其他函数。

您的打印数组函数可能需要:

typedef int (*DataPrinter)(void *ctxt, void *data);
extern int prarray(void *base, size_t nel, size_t width,
                   DataPrinter pr_func, void *ctxt)

数据打印机函数指针将负责打印一个由data参数指定的值。该ctxt值是指向数据打印机功能所需的任何控制信息的指针(它可能像 a 一样简单FILE *,也可能更复杂)。数据打印机函数返回的值是写入的字符数;从返回的值prarray()是写入的字符总数。

当然,这只适用于一维数组。要打印 2D 或 3D 数组的子部分,您需要更复杂的代码。如果您需要担心换行等问题,那很可能是ctxt. 或者您为此功能设计更复杂的接口。请注意,为指定值分隔符提供的唯一机制是通过ctxt结构。这将起作用(或可以使其起作用),但它可能太笨拙了。


C2011 的解决方案_Generic很有趣,但需要 N 个函数用于 N 种类型,每个函数都处理打印一个数组。我无法完全摆脱:我的解决方案需要 N+1 个函数,但其​​中只有一个(1 个)处理数组;N 个函数分别处理打印给定类型的单个值,这比打印给定类型的整个数组更简单。当然,如前所述,它需要在每个相关平台上都有一个 C 2011 编译器。由于至少有一个“经常相关”的平台没有其供应商提供的 C 1999 编译器,因此您可能需要一段时间才能在该平台上使用 C 2011。

于 2012-10-06T00:16:34.877 回答
2

我对同一问题的第二个回答。

您当然可以创建一个函数,如果您将类型信息传递给它,它将知道如何处理数据。因此,您可以使用以下调用约定:

prarray(a, len, 'i'); prarray(b, len, 'c'); prarray(c, len, 'f');

最好对“i”、“c”和“f”使用常量,但您会明白一般的想法。函数定义如下所示:

void parray(void *genericPtr, int len, char typeCode) {
    if (typeCode == 'i') {
        int *intArray = (int*) genericPtr;
        printIntArray(intArray, len);
    }
    if (typeCode == 'c') {
        char *charArray = (char*) genericPtr;
        printString(charArray, len);
    }
    if (typeCode == 'f') {
        float *floatArray = (float*) genericPtr;
        printFloatArray(floatArray, len);
    }
}

注意邪恶的、不安全的类型转换。

于 2012-10-06T00:29:34.937 回答
1

如果没有 C11 编译器,你可以得到的最接近的可能是宏

#define prarray(array, format, length) ({\
    size_t i;\
    size_t _length = length ;
    for (i = 0; i < _length; i++) \
        printf(format ",", (array)[i]);\
})\

invoke with format as your printf format string. e.g. "%c" for character, "%d" for int etc...

于 2012-10-06T00:50:51.597 回答
0

在c中没有多态性。要做到这一点而没有不安全的做法,必须有三个不同的函数传递数组及其长度。IE; void print_ints(int *a,int size); 等等。

于 2012-10-06T00:12:24.720 回答
0

简短的回答是否定的。

更长的答案是您可以创建一个可以包含不同类型值的新数据类型,然后编写一个知道如何将它们打印出来的函数。这会给你你想要的,但是定义你的值在语法上可能会很尴尬。

坦率地说,正是这种问题促使人们发明了动态语言、OOP 等。

我的C真的生锈了,但是...

struct varyThing {
    char typeCode; // indicates what type of data it is.
    union valueUnion {
        long longValue;
        float floatValue;
        char string[32];
    } value;
};

整个结构可能只占用 34 个字节,具体取决于类型大小和字节对齐以及其他编译器细节。

于 2012-10-06T00:19:40.603 回答