简体   繁体   English

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

[英]generic programming in C with void pointer

Even though it is possible to write generic code in C using void pointer(generic pointer), I find that it is quite difficult to debug the code since void pointer can take any pointer type without warning from compiler. 尽管可以使用void指针(泛型指针)在C中编写通用代码,但我发现调试代码非常困难,因为void指针可以在没有编译器警告的情况下采用任何指针类型。 (eg function foo() take void pointer which is supposed to be pointer to struct, but compiler won't complain if char array is passed.) What kind of approach/strategy do you all use when using void pointer in C? (例如,函数foo()接受void指针,它应该是指向struct的指针,但是如果传递char数组,编译器不会抱怨。)在C中使用void指针时,你们都使用什么样的方法/策略?

The solution is not to use void* unless you really, really have to. 解决方案不是使用void*除非你真的,真的必须。 The places where a void pointer is actually required are very small: parameters to thread functions, and a handful of others places where you need to pass implementation-specific data through a generic function. 实际需要void指针的地方非常小:线程函数的参数,以及需要通过泛型函数传递特定于实现的数据的少数其他地方。 In every case, the code that accepts the void* parameter should only accept one data type passed via the void pointer, and the type should be documented in comments and slavishly obeyed by all callers. 在每种情况下,接受void*参数的代码应该只接受通过void指针传递的一种数据类型,并且类型应该记录在注释中并且被所有调用者盲目地遵守。

This might help: 这可能有所帮助:

comp.lang.c FAQ list · Question 4.9 comp.lang.c常见问题列表·问题4.9

Q: Suppose I want to write a function that takes a generic pointer as an argument and I want to simulate passing it by reference. 问:假设我想编写一个将泛型指针作为参数的函数,我想模拟通过引用传递它。 Can I give the formal parameter type void **, and do something like this? 我可以给出形式参数类型void **,并做这样的事情吗?

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

A: Not portably. 答:不便携。 Code like this may work and is sometimes recommended, but it relies on all pointer types having the same internal representation (which is common, but not universal; see question 5.17). 像这样的代码可能有效并且有时会被推荐,但它依赖于具有相同内部表示的所有指针类型(这是常见的,但不是通用的;请参阅问题5.17)。

There is no generic pointer-to-pointer type in C. void * acts as a generic pointer only because conversions (if necessary) are applied automatically when other pointer types are assigned to and from void * 's; C中没有通用的指向指针类型.void *仅作为通用指针使用,因为当其他指针类型与void *分配时,会自动应用转换(如果需要); these conversions cannot be performed if an attempt is made to indirect upon a void ** value which points at a pointer type other than void *. 如果尝试间接在指向void *之外的指针类型的void **值上,则无法执行这些转换。 When you make use of a void ** pointer value (for instance, when you use the * operator to access the void * value to which the void ** points), the compiler has no way of knowing whether that void * value was once converted from some other pointer type. 当您使用void **指针值时(例如,当您使用*运算符访问void **指向的void *值时),编译器无法知道该void *值是否为一次从其他指针类型转换而来。 It must assume that it is nothing more than a void *; 它必须假设它只不过是一个空洞*; it cannot perform any implicit conversions. 它无法执行任何隐式转换。

In other words, any void ** value you play with must be the address of an actual void * value somewhere; 换句话说,您使用的任何void **值必须是某个地方的实际void *值的地址; casts like (void **)&dp, though they may shut the compiler up, are nonportable (and may not even do what you want; see also question 13.9). 类似于(void **)和dp的转换,虽然它们可能会关闭编译器,但是不可移植(甚至可能不能做你想做的事情;另见问题13.9)。 If the pointer that the void ** points to is not a void *, and if it has a different size or representation than a void *, then the compiler isn't going to be able to access it correctly. 如果void **指向的指针不是void *,并且如果它具有与void *不同的大小或表示,则编译器将无法正确访问它。

To make the code fragment above work, you'd have to use an intermediate void * variable: 要使代码片段在上面工作,你必须使用一个中间的void *变量:

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

The assignments to and from vp give the compiler the opportunity to perform any conversions, if necessary. 如果需要,往返于vp的赋值可以让编译器有机会执行任何转换。

Again, the discussion so far assumes that different pointer types might have different sizes or representations, which is rare today, but not unheard of. 同样,到目前为止的讨论假设不同的指针类型可能具有不同的大小或表示,这在今天很少见,但并非闻所未闻。 To appreciate the problem with void ** more clearly, compare the situation to an analogous one involving, say, types int and double, which probably have different sizes and certainly have different representations. 为了更清楚地理解void **的问题,将情况与一个类似的情况进行比较,比如说int和double类型,它们可能有不同的大小,当然也有不同的表示形式。 If we have a function 如果我们有一个功能

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

then we can do something like 然后我们可以做类似的事情

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

and i will be incremented by 1. (This is analogous to the correct void ** code involving the auxiliary vp.) If, on the other hand, we were to attempt something like 并且我将增加1.(这类似于涉及辅助vp的正确的void **代码。)另一方面,如果我们尝试类似的东西

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

(this code is analogous to the fragment in the question), it would be highly unlikely to work. (此代码类似于问题中的片段),它不太可能工作。

Arya's solution can be changed a little to support a variable size: 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;
}

The approach/strategy is to minimize use of void* pointers. 方法/策略是最小化void *指针的使用。 They are needed in specific cases. 在特定情况下需要它们。 If you really need to pass void* you should pass size of pointer's target also. 如果你真的需要传递void *,你也应该传递指针目标的大小。

This generic swap function will help you a lot in understanding generic void * 这个通用交换函数将帮助您理解通用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;
}

We all know that the C typesystem is basically crap, but try to not do that... You still have some options to deal with generic types: unions and opaque pointers. 我们都知道C类型系统基本上是废话,但是尽量不这样做......你仍然有一些选项来处理泛型类型:联合和不透明指针。

Anyway, if a generic function is taking a void pointer as a parameter, it shouldn't try to dereference it!. 无论如何,如果泛型函数将void指针作为参数,它不应该尝试取消引用它!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM