Entiendo el uso del puntero void para la implementación de malloc.Cuándo usar un puntero de vacío?
void* malloc (size_t size);
¿Alguien puede sugerir otras razones o proporcionar algunos escenarios en los que es útil en la práctica.
Gracias
Entiendo el uso del puntero void para la implementación de malloc.Cuándo usar un puntero de vacío?
void* malloc (size_t size);
¿Alguien puede sugerir otras razones o proporcionar algunos escenarios en los que es útil en la práctica.
Gracias
Si la interfaz con el código C y la necesidad de pasar por objeto un C++, pero una biblioteca de C sólo tomará un puntero genérico, a continuación, al recuperar el puntero que necesitan re-fundido a el tipo correcto
Los punteros vacíos probablemente no se deberían usar con mucha frecuencia, pero pueden ayudar cuando se intenta usar una función de biblioteca que funciona con punteros arbitrarios, y realmente no le importa qué datos se representan con esa memoria.
UNA GRAN manera de aprender todo acerca de void * y otros temas de C es ver la primera mitad de los fantásticos "Paradigmas de programación" de Stanford en iTunes-U. ¡Realmente explica el vacío * (C genéricos) y los indicadores en general de manera fantástica! Definitivamente me ayudó a aprender C mejor ...
Uno de los mayores usos es usar void * si quieres poder aceptar diferentes tipos de datos en una función. (aquí está un ejemplo: http://142.132.30.225/programming/node87.html)
He aquí un ejemplo de lo que puede usarlos para:
int i;
char c;
void *the_data;
i = 6;
c = 'a';
the_data = &i;
printf("the_data points to the integer value %d\n", *(int*) the_data);
the_data = &c;
printf("the_data now points to the character %c\n", *(char*) the_data);
Si no quiere ver las clases gratuitas de Stanford, me gustaría recomendar vacío googlear puntero y leyendo todo el material allí.
Los punteros vacíos son útiles cuando se escribe código que debe ejecutarse en múltiples sistemas operativos y debe ser bastante independiente de las API subyacentes del marco.
Por ejemplo, OS X, Windows y Linux tienen el concepto básico de un objeto de ventana, pero todos son muy diferentes. Así que tengo un código común que los pasa como vacíos * y luego implementaciones específicas de plataforma que arrojan el vacío * al tipo nativo (HWND, etc.).
Pero, sí, como han dicho otros en este hilo, este tipo de cosas sin duda se debe evitar, excepto cuando sea necesario.
void
punteros se deben utilizar cada vez que el contenido de un bloque de datos no sea importante. Por ejemplo, al copiar datos, se copia el contenido de un área de memoria, pero el formato de los datos no es importante.
Para las funciones que operan en bloques de memoria sin necesidad de comprender el contenido utilizando void
, los punteros clarifican el diseño a los usuarios para que sepan que a la función no le importa ningún formato de datos. A menudo funciona un código para tomar un char *
para manejar bloques de memoria cuando la función es realmente independiente del contenido.
Mire sqlite3_exec(). Inicia una consulta SQL y desea procesar los resultados de alguna manera (almacenarlos en un contenedor). Llama al sqlite3_exec() y pasa un puntero de devolución de llamada y un puntero void * al objeto que desee (contenedor incluido). Cuando sqlite3_exec() lo ejecuta, llama a la devolución de llamada para cada fila recuperada y pasa ese puntero void * para que la devolución de llamada pueda lanzar el puntero y hacer lo que usted desee.
Lo importante es que a sqlite3_exec() no le importa lo que hace la devolución de llamada ni el puntero que pase. void * es exactamente para tales punteros.
Un buen escenario void*
es cuando desea implementar cualquier ADT genérico, en caso de que simplemente no sepa qué tipo de datos va a mantener y tratar. Por ejemplo, una lista enlazada como la siguiente:
typedef struct node_t node;
struct
{
void* data;
node* prev, next;
} node_t;
typedef struct list_t list;
typedef void* (func)(void*) cpy_func;
typedef void (func)(void*) del_func;
struct
{
node* head, tail, curr;
cpy_func copy;
del_func delete;
} list_t;
initializeLinkedList(cpy_func cpy, del_func del);
//here you keep going defining an API
Aquí, por ejemplo, que pasará de los punteros de función de inicialización de otras funciones que sean capaces de copiar el tipo de datos a su lista y liberándola después. Entonces, al usar void*
, haces que tu lista sea más genérica.
Creo que void*
permaneció en C++ solo por compatibilidad con versiones anteriores, ya que en C++ usted tiene formas más seguras y sofisticadas de lograr el mismo resultado como plantillas, funtores, etc., y no necesita usar malloc mientras programa C++.
En cuanto a C++, no tengo ningún ejemplo útil específico.
Gracias Artem. Habla de la cosa. también en el caso de C, lo mismo puede implementarse usando char *. No pude pensar en un escenario en el que no se pueda usar un char *. (se aplica a malloc). – Mac13
El modismo de C++ consiste en utilizar plantillas, y la biblioteca estándar proporciona muchas y útiles (incluida una lista vinculada). –
Usar 'void *' en lugar de 'char *' proporciona cierta medida de seguridad de tipo. Usted le está diciendo al compilador "Nunca debería permitirme eliminar la referencia de uno de estos indicadores". – Novelocrat
Se utiliza comúnmente en código numérico, por ejemplo una función solver raíz C podría parecerse a lo siguiente:
double find_root(double x0, double (*f)(double, void*), void* params)
{
/* stuff */
y = f(x, params);
/* other stuff */
}
params
es echado por f
a algún tipo de estructura se conoce, pero find_root
no lo hace.
En general, 'void *' hace buenos argumentos para las funciones de devolución de llamada y varias otras cosas donde alguien tendrá que pasar una función de un argumento , pero no debería necesitar saber el tipo de ese argumento. – Novelocrat
Otro ejemplo de este tipo de "genéricos" C, implementados con void *, es una función qsort estándar:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *));
Puede ordenar una matriz de cualquier tipo: int, long, double, char * o algún struct puninters ...
Al lado de la interfaz con C, me encuentro usando punteros void cuando necesito depurar/rastrear algún código y me gusta saber la dirección de un puntero determinado.
SomeClass * myInstance;
// ...
std::clog << std::hex << static_cast< void* >(myInstance) << std::endl;
imprimirá algo así como
0x42A8C410
Y, en mi opinión, muy bien documenta lo que estoy tratando de hacer (saber la dirección del puntero, no se nada acerca de la instancia)
¿Ha considerado usar un depurador? – Eva
int (*f) (void);
f =(void*) getprocaddress(dll,"myfunction");
para hacer el compilador feliz
Esto no se compilará. Cómo va a hacer que el compilador sea feliz no está del todo claro. – AnT
void *
es realmente un C-ismo, y permite C para hacer algo s que no podría razonablemente hacer lo contrario.
char *
no puede ser utilizado de forma portátil para nada, ya que diferentes plataformas pueden hacer diferentes tipos de punteros - un char *
no necesariamente se manejan de la misma (o incluso el mismo tamaño) como void *
.
Entonces, cuando el tipo de datos no se conoce en C (o es polimórfico o dinámico), entonces void *
le permite generar el tipo de puntero subyacente correcto, uno que puede señalar algo correctamente.
En C++ void *
generalmente nunca debe aparecer, excepto en el contexto de la interfaz con el código C heredado de una forma u otra.
Hay una gran ventaja de utilizar el puntero void. La variable de puntero es una variable que almacena la dirección de otra variable. ejemplo:
int a;
int *x= &a;
Ahora 'x' almacena la dirección de la variable de número entero.
Pero éste falla:
float f;
int *x = &f;
Debido variable de puntero de enteros puede almacenar sólo números enteros dirección de la variable. de la misma manera se aplica a otros tipos de datos.
Cuando usa el puntero void *, le da un borde para almacenar la dirección de cualquier variable TYPE.
void *pointer = &i;
void *pointer = &f;
durante la recuperación tiene que ser de referencia.
*((int*)pointer)
Por lo tanto, utilice cuidadosamente el puntero de vacío.
Esto podría ayudarlo, gracias.
En C++, he encontrado que el caso de uso más convincente para void * pointers es dar al código la opción de almacenar "datos de usuario" arbitrarios en un objeto que ya están utilizando.
Digamos que ha escrito una clase que representa un Car
, para usar en software que hace cosas útiles con los objetos Car
(simulación de tráfico, inventario de vehículos de alquiler, lo que sea). Ahora supongamos que se encuentra en una situación en la que su aplicación desea realizar un seguimiento de los contenidos arbitrarios del tronco de un Car
. Los detalles de lo que está almacenado en el troncal no son importantes para la clase Car
, y podrían ser cualquier cosa, realmente depende del propósito de la aplicación que utiliza la clase Auto. Ingrese el puntero void *.
class Car
{
public:
// Existing methods of your Car class
void setContentsOfTrunk(void* contentsOfTrunk);
void* contentsOfTrunk() const;
private:
void* m_contentsOfTrunk;
}
Ahora, cualquier aplicación que utilice la clase Car
tiene la opción de adjuntar un objeto de datos arbitrarios a un objeto existente Car
de tal manera que se puede obtener de cualquier código que tiene el objeto Car
. El contenido del tronco "viaja con" el objeto Car
, donde sea que vaya en su código.
Existen dos alternativas al uso de void * en este caso.
La primera es a la plantilla de su clase en función del tipo de los contenidos troncales del objeto:
template <class TrunkContentsType>
class Car
{
public:
// Existing methods of your Car class
void setContentsOfTrunk(TrunkContentsType contentsOfTrunk);
TrunkContentsType contentsOfTrunk() const;
private:
TrunkContentsType m_contentsOfTrunk;
}
Esto parece innecesariamente invasiva. El tipo de contenido del enlace es importante solo para la aplicación. Los algoritmos y las estructuras de datos que trabajan con objetos de automóvil no se preocupan por lo que hay en el tronco. Al diseñar la clase, está forzando a las aplicaciones que usan la clase a elegir un tipo para el contenido del tronco, pero en muchos casos las aplicaciones tampoco se preocupan por el contenido del tronco.
La segunda alternativa es derivar una nueva clase de coche que añade un miembro de datos y descriptores de acceso para los contenidos troncales:
class Car
{
public:
// Existing methods of your Car class
// No methods having anything to do with trunk contents.
private:
// No data member representing trunk contents.
}
class CarWithTrunkContents
{
public:
// Existing methods of your Car class
void setContentsOfTrunk(TrunkContentsType contentsOfTrunk);
TrunkContentsType contentsOfTrunk() const;
private:
TrunkContentsType m_contentsOfTrunk;
}
La nueva clase CarWithTrunkContents
es una clase específica de la aplicación que añade un miembro de datos de la escriba la aplicación necesita almacenar el contenido del tronco en el automóvil. Esto también parece innecesariamente pesado. ¿Por qué tiene que derivar una clase completamente nueva para agregar un dato adicional que no afecte el comportamiento de la clase? Y si es bastante común que las aplicaciones que utilizan la clase Car
quieran almacenar los contenidos del tronco, ¿por qué obligar a cada aplicación a derivar una nueva clase para su tipo particular de contenido troncal?
Por último, mientras que mi ejemplo artificioso de los contenidos troncales puede que pinta un cuadro vivo de contenidos troncales arbitrarias que viajan con el objeto Car
, en la práctica, lo más probable sería proporcionar un mecanismo aún más general para unir los datos específicos de la aplicación a la Car
:
class Car
{
public:
// Existing methods of your Car class
void setUserData(void* userData);
void* userData() const;
private:
void* m_userData;
}
de esta manera, una aplicación puede adjuntar un objeto que representa el contenido del tronco, o un objeto que representa la licencia de conducir y el registro, o un objeto que representa el contrato de alquiler, o lo que sea. He visto este tipo de puntero void * referido como "userData" (es decir, entendido por el usuario de la clase), "blindData" (es decir, la clase es ciega al contenido del objeto que porta) o "applicationData" (es decir, datos de tipo y finalidad definidos por la aplicación).
Sí, lo conseguimos pero ¿por qué no simplemente crea variables con el tipo de puntero apropiado? ¿Por qué pasar por la molestia de tener que rastrear qué tipo de datos se almacenan en un vacío *? ¿Por qué no utilizas un int * o un char *? – Kekoa
void * son los más útiles cuando se quiere hacer una función genérica (una que toma/devuelve diferentes tipos). ¡Usted puede tener una función que puede tomar ints, o dobles, o longs sin tener que tener una función para cada uno! – micmoo