C语言中的泛型编程与void指针

时间:2010-03-16 15:21:47

标签: c generics pointers

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

6 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:4)

这可能会有所帮助:

comp.lang.c FAQ list · Question 4.9

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

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

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

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

换句话说,你玩的任何void **值必须是某个地方的实际void *值的地址;像(void **)&amp; 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;

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

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

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

答案 2 :(得分:2)

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

答案 3 :(得分:1)

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

答案 4 :(得分:0)

  

这个通用交换函数将帮助您理解通用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;
}

答案 5 :(得分:-2)

我们都知道C类型系统基本上是垃圾,但是尝试不这样做......你仍然有一些处理泛型类型的选项:联合和不透明指针。

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