简体   繁体   中英

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. (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?

The solution is not to use void* unless you really, really have to. 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. 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.

This might help:

comp.lang.c FAQ list · Question 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 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).

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; 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 *. 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. 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; 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). 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.

To make the code fragment above work, you'd have to use an intermediate void * variable:

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.

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. 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

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:

#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. They are needed in specific cases. If you really need to pass void* you should pass size of pointer's target also.

This generic swap function will help you a lot in understanding generic 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.

Anyway, if a generic function is taking a void pointer as a parameter, it shouldn't try to dereference it!.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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