如何在c中找到未知类型数组中的max元素(使用指向函数的指针)

时间:2017-07-17 19:42:58

标签: c arrays algorithm function pointers

我试图编写一个泛型函数,它获取任何类型的数组(intfloatstring)并返回它的最大元素(如果它是一个字符串数组 - 那么词典编纂)。我设法编译它,但是当我运行它时,我得到segmentation fault (core dumped)。谁能告诉我我的代码有什么问题?

#include <stdio.h>
#include <stdlib.h>

void *maxElement(void **arr, int size, int(*comp)(void*, void*)) {
    int i = 0;
    void *max = NULL;

    for (i = 0; i < size; i++) {
        if (comp(arr[i], arr[i + 1])) {
            max = (void*)arr[i];        
        } else
            max = (void*)arr[i + 1];
    }
    return (void*)max;
}

int compareInt(void *a, void *b) {
    int *temp_a = (int*)a, *temp_b = (int*)b;

    if (*temp_a > *temp_b)
        return 1;
    return 0;
}

int main() {
    int i;
    int *result = 0;
    int array[4] = { 1, 3, 7, 0 };
    float array_f[4] = { 1.23, 6.57, 9.89, 11.56 };
    float result_f = 0;
    char string[5][6] = { "jess", "ron", "tom", "mia", "alex" };
    char answer[6];

    result = (int*)maxElement((void*)array, 4, &compareInt);
    printf("result: %d\n", *result);
    return 0;
}

4 个答案:

答案 0 :(得分:5)

你走在正确的轨道上。以下是您的代码中缺少的一些内容:

  • 数组参数的类型应为void*,而不是void**

  • 函数maxElement应该采用元素的数量元素的大小,例如qsort()

  • maxElement函数有缺陷:找不到最大值并访问超出数组末尾的元素。

这些是可选的,但建议更改:

  • 数组参数应该是const限定的,因为您没有修改它。

  • 您应该返回元素索引以避免不必要的强制转换,并且所有索引和大小变量都应该具有类型size_t

  • 为了与qsortbsearch保持一致,比较函数应该使用const void*个参数并返回0表示比较相等的元素,如果是第一个参数是 less 而不是第二个参数,否则为非零正值。

以下是相应的代码:

#include <stdio.h>
#include <string.h>

size_t maxElement(const void *arr, size_t count, size_t size,
                  int(*comp)(const void*, const void*)) {
    size_t i, res = 0;
    const unsigned char *p1 = arr;
    const unsigned char *p2 = p1 + size;

    for (i = 1; i < count; i++, p2 += size) {
        if (comp(p1, p2) < 0) {
            p1 = p2;
            res = i;
        }
    }
    return res;
}

int compareInt(const void *p1, const void *p2) {
    const int *i1 = p1, *i2 = p2;
    return (*i1 > *i2) - (*i1 < *i2);
}

int compareFloat(const void *p1, const void *p2) {
    const float *f1 = p1, *f2 = p2;
    return (*f1 > *f2) - (*f1 < *f2);
}

int compareChars(const void *p1, const void *p2) {
    const char *s1 = p1, *s2 = p2;
    return strcmp(s1, s2);
}

int compareString(const void *p1, const void *p2) {
    const char * const *s1 = p1;
    const char * const *s2 = p2;
    return strcmp(*s1, *s2);
}

int main(void) {
    size_t result;
    int array[4] = { 1, 3, 7, 0 };
    float array_f[4] = { 1.23, 6.57, 9.89, 11.56 };
    char array_s[5][6] = { "jess", "ron", "tom", "mia", "alex" };
    const char *array_str[5] = { "jess", "ron", "tom", "mia", "alex" };

    result = maxElement(array, sizeof(array) / sizeof(*array),
                        sizeof(*array), compareInt);
    printf("int result: %d\n", array[result]);

    result = maxElement(array_f, sizeof(array_f) / sizeof(*array_f),
                        sizeof(*array_f), compareFloat);
    printf("float result: %f\n", array_f[result]);

    result = maxElement(array_s, sizeof(array_s) / sizeof(*array_s),
                        sizeof(*array_s), compareChars);
    printf("string result: %s\n", array_s[result]);

    result = maxElement(array_str, sizeof(array_str) / sizeof(*array_str),
                        sizeof(*array_str), compareString);
    printf("string result: %s\n", array_str[result]);

    return 0;
}

注意array_s(一个固定长度字符数组的数组)和array_str(一个字符串指针数组)之间的区别。它们需要不同的比较功能。

答案 1 :(得分:2)

正如我在chqrlie中提到的评论和answer中所述,原始代码存在许多问题。特别是,像qsort()函数一样,你需要将数组中每个元素的大小传递给函数 - 你应该传递一个值数组,而不是指向值的指针(参见{{3}的注释)我和其他人的反应)。此外,尽管GCC,标准C不允许您在void *上进行指针运算,因为指向的对象的大小是未知的。您需要转换为char *,根据索引和元素大小进行算术运算,然后将其传递给比较函数。

泛型函数需要一些工作来将当前最大值与新条目进行比较(并且仁慈地返回从一个数组返回最后一个值所需的测试数据;我有一个由测试发现的一个一个错误)。

比较器非常不正统 - 它们与qsort()使用的不同,因为它们只返回0或1.最好修改它们以返回负值如果第一个参数比小于第二个,如果第一个参数比第二个大于第二个参数,则为正值;如果参数比较相等,则为零。 maxElements()中的代码可以与任何类型的比较器一起正常工作,但是如果它们符合“标准”,则您可以使用相同的比较器进行排序。 qsort()使用的界面。

进行这些更改以及其他一些更改会产生以下代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void printStr(char **data, size_t num);
static void printDbl(double *data, size_t num);
static void printInt(int *data, size_t num);

int compareInt(const void *a, const void *b);
int compareDbl(const void *a, const void *b);
int compareStr(const void *a, const void *b);
void *maxElement(void *arr, size_t number, size_t size, int (*comp)(const void *, const void *));

void *maxElement(void *arr, size_t number, size_t size, int (*comp)(const void *, const void *))
{
    char *base = arr;
    void *max = base;

    for (size_t i = 1; i < number; i++)
    {
        if (comp(&base[i * size], max) > 0)
            max = &base[i * size];
    }

    return max;
}

int compareInt(const void *a, const void *b)
{
    int i1 = *(int *)a;
    int i2 = *(int *)b;

    return(i1 > i2);
}

int compareDbl(const void *a, const void *b)
{
    double d1 = *(double *)a;
    double d2 = *(double *)b;

    return(d1 > d2);
}

int compareStr(const void *a, const void *b)
{
    char *s1 = *(char **)a;
    char *s2 = *(char **)b;

    int rc = 0;
    if (strcmp(s1, s2) > 0)
        rc = 1;
    return rc;
}

int main(void)
{
    int     i_array[4] = { 11, 3, 7, 0 };
    double  d_array[4] = { 1.23, 6.57, 9.89, 11.56 };
    char   *s_array[5] = { "jess", "ron", "tom", "mia", "alex" };

    printInt(i_array, 4);
    int i_max = *(int *)maxElement(i_array, 4, sizeof(int), &compareInt);
    printDbl(d_array, 4);
    double d_max = *(double *)maxElement(d_array, 4, sizeof(double), &compareDbl);
    printStr(s_array, 5);
    char *s_max = *(char **)maxElement(s_array, 5, sizeof(char *), &compareStr);
    printf("Max integer: %d\n", i_max);
    printf("Max double:  %.2f\n", d_max);
    printf("Max string:  [%s]\n", s_max);
    return 0;
}

static void printStr(char **data, size_t num)
{
    const char *pad = "Strings:";
    for (size_t i = 0; i < num; i++)
    {
        printf("%s %s", pad, data[i]);
        pad = ",";
    }
    putchar('\n');
}

static void printDbl(double *data, size_t num)
{
    const char *pad = "Doubles:";
    for (size_t i = 0; i < num; i++)
    {
        printf("%s %.2f", pad, data[i]);
        pad = ",";
    }
    putchar('\n');
}

static void printInt(int *data, size_t num)
{
    const char *pad = "Integers:";
    for (size_t i = 0; i < num; i++)
    {
        printf("%s %d", pad, data[i]);
        pad = ",";
    }
    putchar('\n');
}

请注意,我选择使用char的1D数组,而不是char *的2D数组。您可以编写代码来处理2D数组;在实践中不太常见。

示例输出:

Integers: 11, 3, 7, 0
Doubles: 1.23, 6.57, 9.89, 11.56
Strings: jess, ron, tom, mia, alex
Max integer: 11
Max double:  11.56
Max string:  [tom]

答案 2 :(得分:2)

以标准C函数bsearch的声明为基础,忽略其第一个参数key

void *bsearch(const void *key, const void *base,
              size_t nmemb, size_t size,
              int (*compar)(const void *, const void *));

因此函数maxElement可以看起来如下

void * maxElement( const void *base, 
                   size_t nmemb, 
                   size_t size, 
                   int comp( const void *, const void * ) ) 
{
    const void *max = base;

    for ( size_t i = 1; i < nmemb; i++ )
    {
        if ( comp( max, ( const char * )base + i * size  ) < 0 )
        {
            max = ( const char * )base + i * size;
        }
    }

    return ( void * )max;
}

这是一个示范程序

#include <stdio.h>
#include <string.h>

void * maxElement( const void *base, 
                   size_t nmemb, 
                   size_t size, 
                   int comp( const void *, const void * ) ) 
{
    const void *max = base;

    for ( size_t i = 1; i < nmemb; i++ )
    {
        if ( comp( max, ( const char * )base + i * size  ) < 0 )
        {
            max = ( const char * )base + i * size;
        }
    }

    return ( void * )max;
}

int compareInt( const void *a, const void *b ) 
{
    int lhs = *( const int * )a;
    int rhs = *( const int * )b;

    return ( rhs < lhs ) - ( lhs < rhs );
}

int compareFloat( const void *a, const void *b ) 
{
    float lhs = *( const float * )a;
    float rhs = *( const float * )b;

    return ( rhs < lhs ) - ( lhs < rhs );
}

int compareString( const void *a, const void *b ) 
{
    return strcmp( a, b );
}

int main( void ) 
{
    int array[4] = { 1, 3, 7, 0 };
    float array_f[4] = { 1.23, 6.57, 9.89, 11.56 };
    char string[5][6] = { "jess", "ron", "tom", "mia", "alex" };

    printf( "%d\n", *( int * )maxElement( array, 4, sizeof( int ), compareInt ) );
    printf( "%f\n", *( float * )maxElement( array_f, 4, sizeof( float ), compareFloat ) );
    printf( "%s\n", ( char * )maxElement( string, 5, sizeof( char[6] ), compareString  ) );

    return 0;
}

它的输出是

7
11.560000
tom

考虑到根据C标准中采用的惯例,比较函数应返回小于,等于或大于零的整数。

答案 3 :(得分:0)

主要问题是,不同类型具有不同的大小,因此数组元素的内存偏移量取决于元素的索引和元素的类型。这意味着如果要访问数组的元素,则必须知道其类型。

在代码中,当您调用maxElement时,您知道数组的类型,并且在比较元素的函数中,但在函数maxElement本身中没有。所以你必须在比较函数中访问数组元素。

void *compareInt(void *a, void *b, int i) {
    int *temp_a = (int*)a;
    int *temp_b = ((int*)b) + i;
    return (temp_a > temp_b) ? a : (void*)temp_b;
}

void *maxElement(void *arr, int size, void*(*comp)(void*, void*, int)) {
    int i;
    void *max = arr;
    for (i = 1; i < size; i++) {
        max = comp( max, arr, i );
    }
    return max;
}

类似的解决方案,不需要sizeof而不需要任何其他参数,如下所示:

int compareInt(void **a, void **b) {
    int *temp_a = (int*)(*a);
    int *temp_b = (int*)(*b);
    temp_b ++;
    *b = (void*)temp_b;
    *a = (*temp_a > *temp_b) ? temp_a : temp_b; 
    return *a != *b;
}

void *maxElement(void *arr, int size, int(*comp)(void**, void**)) {
    int i;
    void *max = arr;
    void *cur = arr;
    for (i = 1; i < size; i++) {
        comp( &max, &cur );
    }
    return max;
}

这可能不是最佳解决方案,但maxElement函数中具有附加类型大小参数的其他解决方案在此问题的其他答案中几乎完美地指出。 我只是想表明一个人可以解决比较函数中的同义问题。