在C中用整数和字符(字符串)数组中的最小元素编写泛型函数

时间:2017-01-16 18:55:01

标签: c arrays algorithm pointers

我正在尝试编写泛型函数来获取整数或字符串数​​组中的最小元素。我正在使用记忆功能。以下是我写的代码:

编辑:修改代码 - 我从int size_t

更改了int size
/* Write a function that returns minimum values of an array of integers or strings */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void returnMinAddress(void *a, void *b, int arr_size, int size) {
  b = a;
  for (int i = 0; i < arr_size; i++) {
    if (memcmp(b, a+((i)*size), size) < 0) {
      memmove(b, a+((i)*size), size);
    }
  }
}
int main() {
  void *b = malloc(sizeof(int));
  /* For an array of type integer */
  int a[8] = {3, 2, 1, -4, 6, 9, 8, -1};
  returnMinAddress(a, b, 8, sizeof(int));
  printf("The result is : %d\n", *(int *)b);
  free(b);
  return 0;
}

花了很多时间之后,我无法理解为什么我一直把答案定为0 ..以下是输出的截图。我在这里缺少什么?

原始代码:

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

void returnMinAddress(void *a, void *b, int arr_size, int size_t) {
  b = a;
  for (int i = 0; i < arr_size; i++) {
    if (memcmp(b, a+((i)*size_t), size_t) < 0) {
      memmove(b, a+((i)*size_t), size_t);
    }
  }
}
int main() {
  void *b = malloc(sizeof(int));
  /* For an array of type integer */
  int a[8] = {3, 2, 1, -4, 6, 9, 8, -1};
  returnMinAddress(a, b, 8, sizeof(int));
  printf("The result is : %d\n", *(int *)b);
  free(b);
  return 0;
}

Output

3 个答案:

答案 0 :(得分:5)

您的通用功能无效。

首先,虽然size_t不是关键字,但它是一个类型说明符。因此,将此单词用作标识符是一个坏主意。

本声明

b = a;

应替换为

memmove( b, a, size_t );

其次根据C标准,您可能不会将指针算法应用于void *类型的指针,因为类型void是不完整类型。所以你应该写例如

( char * )a + i * size_t 

而不是

a+((i)*size_t) 

当函数搜索最小值,然后搜索此语句中的条件

if (memcmp(b, a+((i)*size_t), size_t) < 0) {

应该写成

if ( memcmp( ( char * )a + i * size_t, b, size_t ) < 0) 

考虑到所有这些因素可能看起来像

void returnMinAddress( void *a, void *b, size_t n, size_t m) 
{
    memmove(b, a, m);

    for (size_t i = 1; i < n; i++) 
    {
        if ( memcmp( ( char * )a + i * m, b, m ) < 0 ) 
        {
            memmove(b, ( char * )a + i * m, m );
        }
    }
}

但是还有一个问题是该功能无法解决。被认为是原始字节序列的负整数可能大于正数。因此,如果您将尝试上面显示的数组函数

int a[8] = { 3, 2, 1, -4, 6, 9, 8, -1 };

您将得到最小值等于1,而实际上它等于-4

编写这种通用函数的方法之一是以下

#include <stdio.h>

void * returnMinAddress( const void *a, size_t n, size_t m, int cmp( const void *, const void *) ) 
{
    const void *min = a;

    for (size_t i = 1; i < n; i++) 
    {
        if ( cmp( ( const char * )a + i * m, min ) < 0 ) 
        {
            min = (const char *)a + i * m;
        }
    }

    return (void *)min;
}

int cmp_int(const void *p1, const void *p2)
{
    int a = *(const int *)p1;
    int b = *(const int *)p2;

    return (b < a) - (a < b);
}

int main( void )
{
    int a[8] = { 3, 2, 1, -4, 6, 9, 8, -1 };

    int *b = returnMinAddress(a, 8, sizeof(int), cmp_int);
    printf("The result is : %d\n", *b);
}

程序输出

The result is : -4

答案 1 :(得分:2)

@Vlad from Moscow很好地涵盖了OP代码中的麻烦。

泛型函数提供需要匹配类型和功能的4个参数的替代方法是使用ProgressBar。自C11起可用。 ref

_Generic

// needs 4 arguments. // returnMinAddress(a, b, 8, sizeof(int)); // printf("The result is : %d\n", *(int *)b); 允许代码使用与所需类型匹配的函数 只需要2个参数:指向第一个元素和元素计数的指针。

_Generic

输出

int *min_address_int(int *a, size_t count) {
  if (count == 0) {
    return NULL;
  }
  int *min = a;
  for (size_t i = 1; i < count; i++) {
    if (a[i] < *min) {
      min = &a[i];
    }
  }
  return min;
}

char *min_address_char(char *a, size_t count) {
  if (count == 0) {
    return NULL;
  }
  char *min = a;
  for (size_t i = 1; i < count; i++) {
    if (a[i] < *min) {
      min = &a[i];
    }
  }
  return min;
}

#define min_address(a, sz) (_Generic((a)+0, \
  char *: min_address_char((a)+0, (sz)), \
  int * : min_address_int ((a)+0, (sz)) \
  ))

int main(void) {
  int i[] = {3, 2, 1, -4, 6, 9, 8, -1};
  printf("Min int %d\n", *min_address(i, sizeof i/sizeof i[0]));

  char c[] = {'H', 'e', 'l', 'l', 'o', '!'};
  printf("Min char %c\n", *min_address(c, sizeof c/sizeof c[0]));
  return 0;
}

答案 2 :(得分:0)

主要问题是这一行:

b = a;

执行此操作后,无论何时循环执行:

memmove(b, a+((i)*size_t), size_t);

它覆盖输入数组的第一个元素,而不是复制到调用者的b变量中。最简单的解决方法是将其替换为:

memmove(b, a, size_t);

但相反,我建议使用指针变量指向当前的最小元素。在循环结束时,从该指针复制到调用者的内存。

void returnMinAddress(void *a, void *b, int arr_size, int size_t) {
  void *min = a;
  for (int i = 1; i < arr_size; i++) { // start from 1 because min points to element 0
    if (memcmp(min, a+((i)*size_t), size_t) < 0) {
      min = a+((i)*size_t);
    }
  }
  memmove(b, min, size_t);
}

此外,对void*指针使用算术是GNU扩展。您应该将它们转换为char*以符合标准C.并且使用标准typedef size_t作为变量是糟糕的风格。