¿Qué es uintptr_t
y para qué se puede usar?
¿Qué es uintptr_t
y para qué se puede usar?
Respuestas:
uintptr_t
es un tipo entero sin signo que es capaz de almacenar un puntero de datos. Lo que generalmente significa que tiene el mismo tamaño que un puntero.
Opcionalmente se define en C ++ 11 y estándares posteriores.
Una razón común para querer un tipo entero que pueda contener el tipo de puntero de una arquitectura es realizar operaciones específicas de un entero en un puntero u ocultar el tipo de puntero proporcionándolo como un "identificador" entero.
Editar: Tenga en cuenta que Steve Jessop tiene algunos detalles adicionales muy interesantes (que no voy a robar) en otra respuesta aquí para sus tipos pedantes :)
size_t
solo necesita ser suficiente para contener el tamaño del objeto más grande y puede ser más pequeño que un puntero. Esto se esperaría en arquitecturas segmentadas como el 8086 (16 bits size_t
, pero 32 bits void*
)
ptrdiff_t
. uintptr_t
no es para eso.
unsigned int
generalmente no es lo suficientemente grande. Pero podría ser lo suficientemente grande. Este tipo existe específicamente para eliminar todos los "supuestos" .
Lo primero, cuando se hizo la pregunta, uintptr_t
no estaba en C ++. Está en C99, en <stdint.h>
, como un tipo opcional. Muchos compiladores de C ++ 03 proporcionan ese archivo. También está en C ++ 11, en <cstdint>
, donde nuevamente es opcional, y que se refiere a C99 para la definición.
En C99, se define como "un tipo entero sin signo con la propiedad de que cualquier puntero válido a vacío se puede convertir a este tipo, luego se convierte de nuevo a puntero a vacío, y el resultado se comparará igual al puntero original".
Tome esto para significar lo que dice. No dice nada sobre el tamaño.
uintptr_t
podría ser del mismo tamaño que a void*
. Podría ser más grande. Posiblemente podría ser más pequeño, aunque tal implementación de C ++ se aproxima a perversa. Por ejemplo, en alguna plataforma hipotética donde void*
hay 32 bits, pero solo se usan 24 bits de espacio de direcciones virtuales, podría tener un bit de 24 bits uintptr_t
que satisfaga el requisito. No sé por qué una implementación haría eso, pero el estándar lo permite.
void*
. Sin embargo, afecta las posibles direcciones futuras, especialmente si es posible que desee cambiar para usar algo que realmente es solo un identificador entero, no un puntero convertido en absoluto.
typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;
. En su lugar: callback.dataPtr = (void*)val
. Por otro lado, por supuesto, obtienes void*
y tienes que volver a lanzarlo int
.
Es un tipo entero sin signo exactamente del tamaño de un puntero. Siempre que necesite hacer algo inusual con un puntero, como por ejemplo invertir todos los bits (no pregunte por qué), lo emite uintptr_t
y lo manipula como un número entero habitual, luego lo devuelve.
void*
valor de puntero a uintptr_t
y de nuevo produce un void*
valor que se compara igual al puntero original. uintptr_t
usualmente es del mismo tamaño que void*
, pero eso no está garantizado, ni hay ninguna garantía de que los bits del valor convertido tengan un significado particular. Y no hay garantía de que pueda contener un valor convertido de puntero a función sin pérdida de información. Finalmente, no se garantiza que exista.
Ya hay muchas buenas respuestas a la parte "qué es el tipo de datos uintptr_t". Intentaré abordar el tema "¿para qué se puede utilizar?" parte en esta publicación.
Principalmente para operaciones bit a bit en punteros. Recuerde que en C ++ no se pueden realizar operaciones bit a bit en punteros. Por razones, consulte ¿Por qué no puede realizar operaciones bit a bit en el puntero en C? ¿Hay alguna forma de evitar esto?
Por lo tanto, para realizar operaciones bit a bit en punteros, sería necesario lanzar punteros para escribir unitpr_t y luego realizar operaciones bit a bit.
Aquí hay un ejemplo de una función que acabo de escribir para hacer bitwise exclusivo o de 2 punteros para almacenar en una lista vinculada XOR para que podamos atravesar en ambas direcciones como una lista doblemente vinculada pero sin la penalidad de almacenar 2 punteros en cada nodo .
template <typename T>
T* xor_ptrs(T* t1, T* t2)
{
return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(t1)^reinterpret_cast<uintptr_t>(t2));
}
Corriendo el riesgo de obtener otra insignia de Nigromante, me gustaría agregar un uso muy bueno para uintptr_t (o incluso intptr_t) y eso es escribir código incrustable comprobable. Escribo principalmente código incrustado dirigido a varios procesadores de brazo y actualmente tensilica. Estos tienen varios anchos de bus nativos y la tensilica es en realidad una arquitectura de Harvard con código separado y buses de datos que pueden tener diferentes anchos. Utilizo un estilo de desarrollo basado en pruebas para gran parte de mi código, lo que significa que hago pruebas unitarias para todas las unidades de código que escribo. Las pruebas unitarias en el hardware de destino real son una molestia, por lo que normalmente escribo todo en una PC basada en Intel, ya sea en Windows o Linux usando Ceedling y GCC. Dicho esto, una gran cantidad de código incrustado implica modificaciones de bits y manipulaciones de direcciones. La mayoría de mis máquinas Intel son de 64 bits. Entonces, si va a probar el código de manipulación de dirección, necesita un objeto generalizado para hacer matemáticas. Por lo tanto, uintptr_t le brinda una forma independiente de la máquina de depurar su código antes de intentar implementarlo en el hardware de destino. Otro problema es para algunas máquinas o incluso modelos de memoria en algunos compiladores, los punteros de función y los punteros de datos tienen diferentes anchos. En esas máquinas, es posible que el compilador ni siquiera permita la conversión entre las dos clases, pero uintptr_t debería ser capaz de contener cualquiera.