【问题标题】:generic programming in C with void pointer使用 void 指针在 C 中进行泛型编程
【发布时间】:2011-01-28 04:13:49
【问题描述】:

尽管可以使用 void 指针(通用指针)在 C 中编写通用代码,但我发现调试代码非常困难,因为 void 指针可以采用任何指针类型而不会收到编译器的警告。 (例如,函数 foo() 采用 void 指针,它应该是指向结构的指针,但如果传递了 char 数组,编译器不会抱怨。) 在 C 中使用 void 指针时,你们都使用了什么样的方法/策略?

【问题讨论】:

  • 如果这是一个仅限 C 的问题,请删除 C++ 标记
  • 嗯...“当我用锤子敲击我的手指时会很痛。用锤子敲击身体部位时你使用什么方法?”
  • 如果你认为使用 void * 的 C 泛型编程可以优雅/好用,看看你必须做什么才能使用 qsort 函数...
  • 使用 C++ 模板不是一个可行的选择吗?如果您担心泛型编程,您绝对应该考虑一下。
  • 我在使用 C 的泛型编程领域做了一些研究。我在这篇文章中总结了我的所有发现:andreinc.net/2010/09/30/generic-data-structures-in-c 也许它会有所帮助。

标签: c generics pointers


【解决方案1】:

这可能会有所帮助:

comp.lang.c FAQ list · Question 4.9

问:假设我想编写一个将通用指针作为参数的函数,并且我想模拟通过引用传递它。我可以给形参类型void **,然后做这样的事情吗?

void f(void **);
double *dp;
f((void **)&dp);

A:不便携。像这样的代码可能有效并且有时被推荐,但它依赖于具有相同内部表示的所有指针类型(这是常见的,但不是通用的;参见问题 5.17)。

在 C 中没有泛型指针到指针类型。 void * 充当泛型指针只是因为当其他指针类型分配给 void * 或从 void * 分配时会自动应用转换(如果需要);如果尝试间接指向指向除 void * 之外的指针类型的 void ** 值,则无法执行这些转换。当您使用 void ** 指针值时(例如,当您使用 * 运算符访问 void ** 指向的 void * 值时),编译器无法知道该 void * 值是否曾经从其他一些指针类型转换而来。它必须假定它只不过是一个 void *;它不能执行任何隐式转换。

换句话说,您使用的任何 void ** 值都必须是某处实际 void * 值的地址;像 (void **)&dp 这样的强制转换,虽然它们可能会关闭编译器,但它们是不可移植的(甚至可能无法执行您想要的操作;另请参见问题 13.9)。如果 void ** 指向的指针不是 void *,并且如果它的大小或表示与 void * 不同,那么编译器将无法正确访问它。

要使上面的代码片段正常工作,您必须使用中间 void * 变量:

double *dp;
void *vp = dp;
f(&vp);
dp = vp;

与 vp 之间的分配使编译器有机会在必要时执行任何转换。

同样,到目前为止的讨论假设不同的指针类型可能具有不同的大小或表示,这在今天是罕见的,但并非闻所未闻。为了更清楚地理解 void ** 的问题,请将这种情况与涉及类型 int 和 double 的类似情况进行比较,它们可能具有不同的大小并且当然具有不同的表示。如果我们有一个函数

void incme(double *p)
{
    *p += 1;
}

然后我们可以做类似的事情

int i = 1;
double d = i;
incme(&d);
i = d;

并且 i 将增加 1。(这类似于涉及辅助 vp 的正确 void ** 代码。)另一方面,如果我们要尝试类似

int i = 1;
incme((double *)&i);    /* WRONG */

(此代码类似于问题中的片段),它极不可能工作。

【讨论】:

    【解决方案2】:

    我们都知道 C 类型系统基本上是垃圾,但尽量不要那样做……你仍然有一些选择来处理泛型类型:联合和不透明指针。

    无论如何,如果泛型函数将 void 指针作为参数,它不应该尝试取消引用它!

    【讨论】:

      【解决方案3】:

      解决方案是不要使用void*,除非你真的、真的必须这样做。实际需要 void 指针的地方非常小:线程函数的参数,以及其他一些需要通过泛型函数传递特定于实现的数据的地方。在每种情况下,接受 void* 参数的代码应该只接受通过 void 指针传递的 一个 数据类型,并且该类型应该记录在 cmets 中,并被所有调用者严格遵守。

      【讨论】:

      • OP 特别提到了泛型编程,这是 void* 的一个很好的用例(在没有模板或漂亮宏等高级语言工具的情况下。)
      • 我的意思是,例如,qsort() 和 bsearch()
      • @Matt、qsort 和 bsearch 是“通过通用函数实现特定数据”的示例。
      • @JS:是的,当你在做泛型编程时,你需要做的地方的数量接近于一辆满载的卡车而不是少数;-)
      【解决方案4】:

      方法/策略是尽量减少使用 void* 指针。在特定情况下需要它们。如果你真的需要传递 void* 你也应该传递指针目标的大小。

      【讨论】:

        【解决方案5】:

        这个通用交换函数将帮助您理解通用 void *

        #include<stdio.h>
        void swap(void *vp1,void *vp2,int size)
        {
                char buf[100];
                memcpy(buf,vp1,size);
                memcpy(vp1,vp2,size);
                memcpy(vp2,buf,size);  //memcpy ->inbuilt function in std-c
        }
        
        int main()
        {
                int a=2,b=3;
                float d=5,e=7;
                swap(&a,&b,sizeof(int));
                swap(&d,&e,sizeof(float));
                printf("%d %d %.0f %.0f\n",a,b,d,e);
        return 0;
        }
        

        【讨论】:

          【解决方案6】:

          Arya 的解决方案可以稍作改动以支持可变大小:

          #include <stdio.h>
          #include <string.h>
          
          void swap(void *vp1,void *vp2,int size)
          {
            char buf[size];
            memcpy(buf,vp1,size);
            memcpy(vp1,vp2,size);
            memcpy(vp2,buf,size);  //memcpy ->inbuilt function in std-c
          }
          
          int main()
          {
            int array1[] = {1, 2, 3};
            int array2[] = {10, 20, 30};
            swap(array1, array2, 3 * sizeof(int));
          
            int i;
            printf("array1: ");
            for (i = 0; i < 3; i++)
              printf(" %d", array1[i]);
            printf("\n");
          
            printf("array2: ");
            for (i = 0; i < 3; i++)
              printf(" %d", array2[i]);
            printf("\n");
          
            return 0;
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-03-11
            • 2019-06-05
            • 2010-10-16
            • 2018-06-08
            • 1970-01-01
            • 1970-01-01
            • 2012-07-07
            相关资源
            最近更新 更多