使用泛型参数包装算法的最安全,最可读的方法是什么?

时间:2011-06-17 14:43:22

标签: c algorithm generics type-conversion

简介

我有一个算法,它带有一个指向char数组的指针。该算法首先检索数组的长度,然后反转数组。

问题

我遇到的问题是我想在wchar_t数组上使用它。并且希望能够在不必复制整个函数的情况下执行此操作,更改参数的名称和类型。

以下是上述功能:

void reverseString(char *str){
  unsigned int l = getStringLength(str);
  int i = 0;
  int m = l >> 1;

  while(i < m){
    str[i] ^= str[l - 1];
    str[l - 1] ^= str[i];
    str[i] ^= str[l - 1];
    i++;
    l--;
  }
}

从谷歌搜索和阅读SO这将无法使用void指针(概念上使用union),因为它会给我一个这样的解决方案,这对我来说与编写单独的函数但使用不同的名称和参数类型一样糟糕:

void reverseString(void *array, short typeSize){
  unsigned int l = getArrayLength(array);
  int m = l >> 1;
  int i = 0;
  char *str = 0;
  wchar_t *wstr = 0;

  if(typeSize == 1){
    str = (char *) array;
    while(i < m){
      str[i] ^= str[l - 1];
      str[l - 1] ^= str[i];
      str[i] ^= str[l - 1];
      i++;
      l--;
    }
  }else if(typeSize == 4){
    wstr = (wchar_t *) array;
    while(i < m){
      wstr[i] ^= wstr[l - 1];
      wstr[l - 1] ^= wstr[i];
      wstr[i] ^= wstr[l - 1];
      i++;
      l--;
    }
  }
}

注意:getStringLength只是一个循环遍历指针的函数,直到它到达'\0'并返回迭代总和。

答案

我正在寻找一个答案,告诉我如何以更好的方式做到这一点,而不必重写算法的内部,或者回答说不可能以任何其他方式做到这一点。我不是在寻找答案,告诉我应该使用这个和那个为我做这个的库,因为我没有在生产代码中使用它,因此更好地理解内存管理的工作方式和其他概念是纯粹的教育一样。

编辑:我展示的功能只是一个例子,我正在寻找算法问题的通用解决方案。

3 个答案:

答案 0 :(得分:2)

它不太可能有效,但您可以轻松地让void reverseString(void *array, short typeSize)版本通过简单的指针算法和相关大小的memcpy来反转元素。

当然,这种方法并不适用于您希望与类型无关的每种算法。从你的问题中不清楚你是否只关心这个特定的算法,或者一般的算法。

[旁白:请注意,使用XOR交换不太可能比“天真地”更有效。它肯定不太可读!]

答案 1 :(得分:2)

在C中使用“泛型”可能会产生比原始代码明显更慢且更复杂/难以阅读/难以维护的代码。如果必须执行此操作,请使用预处理器。

我的建议是尽可能避免这种技术:你应该只在程序中使用charwchar_t,而不是两者的混合! (charUChar或几乎普遍优先,因为您可以选择编码,但我离题...)

#define gchar char
#define gstrlen strlen
#define func_name reverse
#include "reverse_impl.h"
#undef gchar
#undef gstrlen
#undef func_name

#define gchar wchar_t
#define gstrlen wstrlen
#define func_name wreverse
#include "reverse_impl.h"
#undef gchar
#undef gstrlen
#undef func_name

然后,在reverse_impl.h中:

void func_name(gchar *str)
{
    gchar *p = str, *q = str + gstrlen(str), t;
    if (p == q)
       return;
    q--;
    for (; p < q; p++, q--) {
        t = *p;
        *p = *q;
        *q = t;
    }
}

此外,请勿这样做:

x ^= y; // bad!
y ^= x;
x ^= y;

执行起来更难以阅读慢得多

另请注意,reversewreverse如果您为其提供Unicode输入,则会产生垃圾:reverse会输出格式错误,wreverse可以切换变音符号或完全搞砸韩文,取决于他们的代表方式。

答案 2 :(得分:1)

  
    

......通用解决方案......

  

解决方案是编写类似qsort()的东西:所有需要知道各个值大小的函数都会传递给你自己的函数,并指向虚无处理的

#include <math.h>
#include <stdio.h>
#include <wchar.h>

void universalReverseArray(void *arr, size_t siz,
                           size_t (*arrlen)(void*),
                           void (*swap)(void*, void*))
{
  size_t elems = arrlen(arr);
  size_t i = 0;
  size_t m = elems >> 1;
  unsigned char *p = arr;

  while(i < m) {
    swap(p + i * siz, p + (elems - 1) * siz);
    i++;
    elems--;
  }
}

void cswap(void *a, void *b) {
  char *aa = a, *bb = b;
  char t = *aa;
  *aa = *bb;
  *bb = t;
}

void dswap(void *a, void *b) {
  double *aa = a, *bb = b;
  double t = *aa;
  *aa = *bb;
  *bb = t;
}

void wswap(void *a, void *b) {
  wchar_t *aa = a, *bb = b;
  wchar_t t = *aa;
  *aa = *bb;
  *bb = t;
}

size_t clen(void *arr) {
  char *aa = arr;
  size_t retval = 0;
  while (*aa) {
    retval += 1;
    aa += 1;
  }
  return retval;
}

size_t dlen(void *arr) {
  double *aa = arr;
  size_t retval = 0;
  while (fabs(*aa) >= 0.0001) {
    retval += 1;
    aa += 1;
  }
  return retval;
}

size_t wlen(void *arr) {
  wchar_t *aa = arr;
  size_t retval = 0;
  while (*aa) {
    retval += 1;
    aa += 1;
  }
  return retval;
}

int main(void) {
  double x[] = {1, 2, 3, 4, 5, 0};
  char y[] = "foobar";
  wchar_t z[4];
  z[0] = 'a'; z[1] = 'b'; z[2] = 'c'; z[3] = 0;

  for (int k=0; k<5; k++) {printf("%f ", x[k]);}
  printf("%s ", y);
  printf("%ls\n", z);

  universalReverseArray(x, sizeof *x, dlen, dswap);
  universalReverseArray(y, sizeof *y, clen, cswap);
  universalReverseArray(z, sizeof *z, wlen, wswap);

  for (int k=0; k<5; k++) {printf("%f ", x[k]);}
  printf("%s ", y);
  printf("%ls\n", z);

  return 0;
}

您可以在ideone上看到它“正在运行”:http://ideone.com/t1iOg