2010-03-16 14 views
10

Aunque es posible escribir código genérico en C usando un puntero void (puntero genérico), encuentro que es bastante difícil depurar el código ya que el puntero void puede tomar cualquier tipo de puntero sin advertencia del compilador (por ejemplo, la función foo() toma el puntero void que se supone puntero a struct, pero el compilador no se queja si se pasa la matriz char.) ¿Qué tipo de enfoque/estrategia utilizas cuando utilizas el puntero void en C?programación genérica en C con puntero de vacío

+4

Si esto es una pregunta C solo quita la etiqueta C++ –

+13

Hmmm ... "Cuando golpeo mi dedo con un martillo duele a lo grande. ¿Qué enfoque utilizas cuando golpeas partes de tu cuerpo con un martillo?" – sharptooth

+2

Si crees que la programación C generic con void * puede ser elegante/agradable de usar, mira lo que tienes que hacer para usar la función qsort ... –

Respuesta

8

La solución es no utilizar void* a menos que realmente, realmente tenga que hacerlo. Los lugares donde realmente se requiere un puntero void son muy pequeños: parámetros para las funciones de subproceso y un puñado de otros lugares donde debe pasar datos específicos de la implementación a través de una función genérica. En todos los casos, el código que acepta el parámetro void* solo debe aceptar un tipo de datos pasado a través del puntero void, y el tipo debe documentarse en los comentarios y ser obedecido servilmente por todos los llamantes.

+4

El OP mencionó específicamente la programación genérica, que es un caso de uso bastante bueno para void * (en ausencia de instalaciones de lenguaje de nivel superior como plantillas o macros agradables). –

+0

Es decir, por ejemplo , qsort() y bsearch() –

+0

@Matt, qsort y bsearch son ejemplos de "datos específicos de implementación a través de una función genérica". –

4

Esto podría ayudar:

comp.lang.c FAQ list · Question 4.9

Q: Supongamos que quiero escribir una función que toma un puntero genérico como un argumento y quiero simular pasándolo por referencia. ¿Puedo dar el tipo de parámetro formal void **, y hacer algo como esto?

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

A: Not portably. Código como este puede funcionar y, a veces, se recomienda, pero se basa en todos los tipos de punteros que tienen la misma representación interna (que es común, pero no universal, ver pregunta 5.17).

No hay ningún tipo genérico de puntero a puntero en C. void * actúa como un puntero genérico solo porque las conversiones (si es necesario) se aplican automáticamente cuando se asignan otros tipos de puntero ay desde void * 's; estas conversiones no se pueden realizar si se intenta indirectamente sobre un valor nulo ** que apunta a un tipo de puntero distinto de void *. Cuando utiliza un valor de puntero void ** (por ejemplo, cuando utiliza el operador * para acceder al valor nulo * al que apunta el vacío **), el compilador no tiene forma de saber si ese valor nulo * fue alguna vez convertido desde algún otro tipo de puntero. Debe suponer que no es más que un vacío *; no puede realizar conversiones implícitas.

En otras palabras, cualquier valor de nulo ** con el que juegue debe ser la dirección de un valor nulo * real en alguna parte; los lanzamientos como (nulo **) & dp, aunque pueden cerrar el compilador, no son portables (y es posible que ni siquiera hagan lo que quieran, consulte también la pregunta 13.9). Si el puntero al que apunta el vacío ** no es un vacío *, y si tiene un tamaño o representación diferente a un vacío *, entonces el compilador no podrá acceder a él correctamente.

Para hacer que el fragmento de código anterior trabajo, que tendría que utilizar un vacío intermedio variables *:

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

Las asignaciones hacia y desde vp dar el compilador la oportunidad de realizar ninguna conversión, si es necesario.

Una vez más, la discusión asume que diferentes tipos de punteros pueden tener diferentes tamaños o representaciones, lo cual es raro hoy en día, pero no es algo inaudito. Para apreciar el problema con el vacío ** más claramente, compare la situación con una análoga que involucre, digamos, tipos int y doble, que probablemente tengan diferentes tamaños y ciertamente tengan diferentes representaciones. Si tenemos una función

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

entonces podemos hacer algo como

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

y voy a estar incrementado en 1. (Esto es análogo al vacío correcta ** código que implica la vp auxiliar.) Si, por el contrario, estábamos a intentar algo así como

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

(este código es análogo al fragmento en la pregunta), sería muy poco probable que funcione.

-2

Todos sabemos que el sistema de tipos C es básicamente basura, pero trate de no hacer eso ... Todavía tiene algunas opciones para tratar con tipos genéricos: uniones y punteros opacos.

De todos modos, si una función genérica está tomando un puntero de vacío como parámetro, ¡no debería intentar desreferenciarlo !.

1

El enfoque/estrategia es minimizar el uso de punteros void *. Se necesitan en casos específicos. Si realmente necesita pasar el vacío *, también debe pasar el tamaño del objetivo del puntero.

0

Esta función de intercambio genérico le ayudará mucho en la comprensión de vacío genérica *

#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; 
} 
+2

¿Qué ocurre si el tamaño es> 100? –

2

solución de Arya se puede cambiar un poco para apoyar un tamaño variable:

#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; 
} 
Cuestiones relacionadas