在C中对数组实现通用的“map”函数

时间:2010-10-28 21:48:48

标签: c arrays map function-pointers

我很难在数组上实现通用的'map'函数。 我从以下草案开始:

void MapArray(void * src, void * dest, void * (f)(void *), size_t n, size_t elem)
{
   unsigned int i = 0, j = 0;
   void * temp = malloc(elem);

   for(i = 0; i<n, i++)
   {
      temp = (f)((char *) src) + i));
      for(j = 0; j < elem; j++)
      {
         *(((char *) dest) + i) = *(((char *) temp) + i);
      }
   }
   free(temp);
}

我理解为什么它不正确 - 我会在给它'f'之前投入(char *) - 但我现在已经失去动力并且无法提出解决方案。 (我在学习C的过程中这样做)

我的理由是获得'f'的结果,并逐字节地将其复制到dest [i]。

你能给我一些提示吗?

3 个答案:

答案 0 :(得分:18)

你的第一个问题是,你在一些表达方式中做得太多了。你需要分解它。

void MapArray(void * src, void * dest, void * (f)(void *), size_t n, size_t elem)
{
   unsigned int i = 0, j = 0;
   void * temp = malloc(elem);

   char* csrc = (char*)src;
   char* cdest = (char*)dest;
   char* ctemp = (char*)temp;
   for(i = 0; i<n; i++)
   {
       csrc++;
       cdest++;
       ctemp++;
       temp = f(csrc);
       for(j = 0; j < elem; j++)
       {
           cdest[i] = ctemp[i];
       }
   }
   free(temp);
}

现在是你的第二个问题。你malloc一个缓冲区,然后你..分配给那个指针?反复?然后只释放最后一个f调用的结果?这完全没必要。

void MapArray(void * src, void * dest, void * (f)(void *), size_t n, size_t elem)
{
   unsigned int i = 0, j = 0;

   char* csrc = (char*)src;
   char* cdest = (char*)dest;
   for(i = 0; i<n; i++)
   {
       csrc++;
       cdest++;
       char* ctemp = (char*)f(csrc);
       for(j = 0; j < elem; j++)
       {
           cdest[i] = ctemp[i];
       }
   }
}

现在是你的第三个问题。你传入指针 - 但只传给char。你没有传递空白*。这意味着您的函数不能是通用的 - f不能应用于任何东西。我们需要一个void * s数组,以便该函数可以将任何类型作为参数。我们还需要将类型的大小作为参数,以便我们知道向dest移动的距离。

void MapArray(void ** src, void * dest, void * (f)(void *), size_t n, size_t sizeofT)
{
    for(unsigned int i = 0; i < n; i++) {
        void* temp = f(src[n]);
        memcpy(dest, temp, sizeofT);
        dest = (char*)dest + sizeofT;
    }
}

我们还有另一个问题 - 临时的记忆。我们不释放它。我们也不会将用户数据参数传递给f,这将允许它返回我们不需要释放的堆分配内存。 f可以工作的唯一方法是返回一个静态缓冲区。

void MapArray(void ** src, void * dest, void * (f)(void *, void*), void* userdata, size_t n, size_t sizeofT)
{
    for(unsigned int i = 0; i < n; i++) {
        void* temp = f(src[n], userdata);
        memcpy(dest, temp, sizeofT);
        dest = (char*)dest + sizeofT;
    }
}

现在f可以运行几乎任何它喜欢的东西,并保持它需要的任何状态。但我们仍然没有释放缓冲区。现在,f返回一个简单的结构,告诉我们是否需要释放缓冲区。这也允许我们在不同的f。

调用中释放或不释放缓冲区
typedef struct {
    void* data;
    int free;
} freturn;

void MapArray(void ** src, void * dest, freturn (f)(void *, void*), void* userdata, size_t n, size_t sizeofT)
{
    for(unsigned int i = 0; i < n; i++) {
        freturn thisreturn = f(src[n], userdata);
        void* temp = thisreturn.data;
        memcpy(dest, temp, sizeofT);
        dest = (char*)dest + sizeofT;
        if (thisreturn.free)
            free(temp);
    }
}

但是,我仍然不明白这个功能的目的。所有这些都取代了简单的for循环?您尝试替换的代码比调用函数的代码更简单,并且可能更高效,并且肯定更强大(例如,它们可以使用continue / break)。

更重要的是,C真的很糟糕这种工作。 C ++好多了。例如,将函数应用于数组的每个成员都非常简单。

答案 1 :(得分:2)

有一个原因,在C标准库中没有这么多 - 在C语言中几乎不可能做到这一点。你不能将结果“逐字节”复制到dest[i] - 由于您已将dest投射到char *,因此它只指向一个char(字节)。

据推测,elem的大小是f返回的大小,而nsrcdest中元素的数量。在这种情况下,你的代码并不太远,但是(正如你已经猜测的那样)你操纵指针的方式(特别是转换为char *)不会削减它。 / p>

然而,即使你修复了这个问题,你还会遇到另一个问题:从f分配返回类型,而不知道类型真的(真的)很难。事实上,关于我能想到的唯一方法是将这些代码包装成宏而不是:

#define MapArray(s, d, f, n) \
do {                         \
   unsigned i;               \
   for (i = 0; i<n; i++)     \
      d[i] = f(s[i]);        \
} while (0)

您可以使用以下内容:

int f(int a) { return a + 100; }

#define elements(array) (sizeof(array)/sizeof(array[0]))

int main() { 
    unsigned i;
    int x[] = { 0, 1, 2, 3};
    int y[elements(x)];

    MapArray(x, y, f, elements(x));

    for (i=0; i<elements(x); i++)
        printf("%d\n", y[i]);
    return 0;
}

注意:我推荐这个。这个一种方法来做你所要求的,但正如我所说的那样,这几乎不可能在C中做得很好。这段代码在某种程度上有效,但IMO确实如此没有资格做好这份工作。

答案 2 :(得分:1)

  • 检查函数f是否未返回指向局部变量的指针
  • 我不确定j上的循环应该是什么