【问题标题】:Generic function to print an array in C在 C 中打印数组的通用函数
【发布时间】:2014-11-20 03:10:53
【问题描述】:

我正在尝试编写一个通用函数来打印数组的元素。这就是我所拥有的:

void printArray(void * source, int numElements, int size, void (*printFunction)(void *)){

     void *dest[numElements];
     memcpy(dest, &source, (size * nElem));
     int i;
     for (i = 0; i < numElements; i++){
         printFunction(dest[i]);
     }
 }

我传入了一个数组的地址、数组中的元素数量、数组中每个元素的大小,以及一个回调函数来处理数组类型的打印/格式化(打印函数效果很好,我已经测试过了)。

现在它正在打印第一个元素,然后我遇到了分段错误。我无法弄清楚问题所在 - 任何帮助将不胜感激。我是stackoverflow的新手,所以如果我能改进我的问题,请告诉我。

【问题讨论】:

  • dest 是一个空指针数组。我不认为那是你想要的。请改用char dest[numElements*size]
  • 请说明你是如何调用函数的。
  • 我不明白为什么你需要 memcpy anything 来完成这项任务。

标签: c arrays generics printing dynamic-arrays


【解决方案1】:

你为什么要抄袭?

你真正想要的是传递一个 char 指针,这样你就可以将它递增 size0numElements 次,每次都传递给 printFunction。

请注意,您实际上编写了一个比仅打印数组的函数更通用的函数。您实际上是在编写一个将函数映射到数组的“映射”函数。

这样的东西应该可以工作(注意它未经测试):

void printArray(void * source, int numElements, int size, void (*printFunction)(void *)){
    int i;
    for (i = 0; i < numElements; i++){
        printFunction(((char *)source) + i * size);
     }
}

【讨论】:

  • char 指针不需要作为参数。您可以通过原始void *source 铸造来制造它。 IE。 printFunction((char*)source + i*size); 就足够了。如果我要更改除此之外的任何内容,那就是为sizei 使用无符号类型,最好是size_t
  • 是的,但char * 与签名中的void * 一样好。如果 OP 想要,他们可以将其转换为 char *,但我看不出有什么理由不把它放在签名中。不过,我同意你的 size_t 建议。
  • "为什么不把它放在签名开头" - 原因很简单,在这个函数中强制转换一次或到处 b> 这个函数是用任何char *以外的东西调用的。通用函数(qsort、bsearch 等)采用void* 是有原因的。
  • 参数列表中的char *不如void *;您必须显式转换(转换)指向char * 的指针,但转换为void * 会自动发生。最好将void * 保留在参数列表中,然后创建char *src = source; 并在之后使用src + i * size。但我 100% 同意“为什么要复制”的评论。
  • 啊,好点子。我忘记了隐式转换。我会更新我的答案。
【解决方案2】:

我假设source 是数组开头的void *(而不是指针数组)。那样的话……

无需复制数组——您可以使用source 本身。由于允许在 C 中修改函数参数并且只影响函数内的副本,因此您可以在继续遍历数组时更改 sourcenumElements。考虑到这些要点,您可以将函数简化为以下内容:

void printArray(void *source, int numElements, 
                    int size, void (*printFunction)(void *))
{
    for (; numElements; numElements--) {
        printFunction(source);
        source = ((char *)source) + size;
    }
}

上面的版本运行for 循环,递减numElements 为每个数组元素提供一次通过。在每次传递结束时,sourcesize 偏移以到达下一个元素,然后将其传递给您的回调函数。即使sourcevoid *(char *) 强制转换用于允许指针运算。

【讨论】:

    【解决方案3】:

    您应该使用char 数组来为您的数据设置别名,而不是void* 数组。后者是一个指针数组,不适合作为缓冲区的类型。

    一个泛型函数应该看起来更像这样:

    void printArray(void * source, int numElements, int size, void (*printFunction)(void *)){
    
         char dest[numElements*size];
         memcpy(dest, source, (size * numElements)); // source is already a pointer, don't address it
         int i;
         for (i = 0; i < numElements; i++){
             printFunction(dest[i*size]); //make sure you index based on the type and not just a single byte
         }
     }
    

    【讨论】:

      【解决方案4】:

      它更简单,不需要乘法,只需指针运算:

      void printArray(void *source, int numElements, int size, void (*printFunc)(void *)){
        while (numElements--) {
          printFunc(source);
          source=(char *)source+size; //C forbids void* math. source+=size fails on MSVC
        }
      }
      

      【讨论】:

        【解决方案5】:
        void printArray(void * source, int numElements, int size, void (*printFunction)(void *)){
            int i;
            for (i = 0; i < numElements; i++){
                printFunction((source + i * size));
            }
        }
        

        在您的程序中,您将源内容保存到 void 指针数组中,因此在打印时会出现段错误。相反,您可以直接使用源代码,为了获取每个元素,您必须将大小添加到源代码。

        【讨论】:

          猜你喜欢
          • 2022-12-04
          • 2011-08-03
          • 1970-01-01
          • 1970-01-01
          • 2013-09-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多