【问题标题】:Writing a generic function for minimum element in arrays of integers and characters(strings) in C在C中为整数和字符(字符串)数组中的最小元素编写通用函数
【发布时间】:2017-05-31 16:41:21
【问题描述】:

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

编辑:修改代码 - 我从 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;
}

【问题讨论】:

  • 这是什么:a+((i)*size_t)???它甚至可以编译吗?你知道size_t 是一个类型名称吗?
  • 在执行b = a; 之后,您的memmove() 将覆盖数组,而不是写入调用者的b 变量。
  • 旁注:除非您 100% 确定自己知道自己在做什么,否则不要对结构执行此操作。填充、魔术字节等将导致“相同”结构错误地使memcmp() 操作失败。
  • 更一般地,不能依赖memcmp() 产生与应用于声明的指向对象类型的常规比较运算符等效的结果,除非被比较的对象是单独的unsigned char s.
  • @EOF,我相当有信心这是一个范围界定问题。 OP 的 size_t 的作用域是声明的函数;在那里,它可以隐藏相同标识符的文件范围类型定义。但从多个方面来看,它仍然是一种糟糕的形式。

标签: c arrays algorithm pointers


【解决方案1】:

主要问题是这一行:

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 作为变量是不好的风格。

【讨论】:

  • 在尝试了您的解决方案后,我得到的答案是 -1 而不是 -4。您能否解释一下这是为什么?
  • 两个问题:你有 memcmp() 的论据,以及@VladfromMoscow 的答案最后一段中描述的问题
【解决方案2】:

您的通用函数无效。

首先虽然size_t 不是关键字,但它是一个类型说明符。所以用这个词作为标识符是个坏主意。

此声明

b = a;

应该替换为

memmove( b, a, size_t );

其次,根据 C Stndard,您可能不会将指针算法应用于 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

【讨论】:

  • 感谢您的回答。如果我想写一个泛型函数,那我应该怎么做呢?有什么建议吗?
【解决方案3】:

@Vlad from Moscow 很好地解决了 OP 代码中的问题。

为需要匹配类型和功能的通用函数提供4个参数的替代方法是使用_Generic。从 C11 开始可用。 ref

// needs 4 arguments.  
// returnMinAddress(a, b, 8, sizeof(int));
// printf("The result is : %d\n", *(int *)b);

_Generic 允许代码使用与所需类型匹配的函数。
只需要 2 个参数:指向第一个元素的指针和元素计数。

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;
}

输出

Min int -4
Min char !

【讨论】:

  • 很少有提到泛型选择的情况。:)
猜你喜欢
  • 2014-11-28
  • 2018-03-22
  • 1970-01-01
  • 2013-12-17
  • 2021-03-28
  • 2017-06-02
  • 2020-02-25
  • 2011-05-31
  • 1970-01-01
相关资源
最近更新 更多