¿Qué es el tipo de datos uintptr_t?

10 minutos de lectura

avatar de usuario de dimba
dimba

Que es uintptr_t y para que se puede usar?

Avatar de usuario de Steve Jessop
steve jesop

Lo primero, en el momento en que 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 para anular se puede convertir a este tipo, luego volver a convertirlo en puntero para anular, y el resultado será igual al puntero original”.

Toma esto en el sentido de lo que dice. No dice nada sobre el tamaño.

uintptr_t puede ser del mismo tamaño que un void*. Podría ser más grande. Posiblemente podría ser más pequeño, aunque tal implementación de C++ se aproxima a lo perverso. Por ejemplo en alguna plataforma hipotética donde void* es de 32 bits, pero solo se usan 24 bits de espacio de direcciones virtuales, podría tener un espacio de 24 bits uintptr_t que satisface el requisito. No sé por qué una implementación haría eso, pero el estándar lo permite.

  • Gracias por el ““. Mi aplicación no se compiló debido a la declaración uintptr_t. Pero cuando leo tu comentario, agrego “#include ” y sí, ahora funciona. ¡Gracias!

    – Corredor de Java

    12 de febrero de 2014 a las 8:13


  • ¿Para asignar memoria alineada, entre otros usos, por ejemplo?

    – leyendas2k

    21 de julio de 2015 a las 7:36

  • Otro caso de uso común de convertir un puntero en un int es crear un controlador opaco que oculta el puntero. Esto es útil para devolver una referencia a un objeto desde las API donde desea mantener el objeto privado para la biblioteca y evitar que las aplicaciones tengan acceso a él. Luego, la aplicación se ve obligada a usar la API para realizar cualquier operación en el objeto.

    – Joel Cunningham

    5 mayo 2016 a las 13:57

  • @JoelCunningham: eso funciona pero en realidad no es diferente de usar void*. Sin embargo, afecta las posibles direcciones futuras, especialmente si desea cambiar para usar algo que realmente es solo un identificador de número entero, no un puntero convertido en absoluto.

    –Steve Jessop

    5 mayo 2016 a las 14:19


  • @CiroSantilli烏坎事件2016六四事件法轮功 Un caso de uso común es pasar solo un int a una API que espera un vacío* en los datos genéricos. Te ahorra las pulsaciones de teclas typedef struct { int whyAmIDoingThis; } SeriouslyTooLong; SeriouslyTooLong whyAmNotDoneYet; whyAmINotDoneYet.whyAmIDoingThis = val; callback.dataPtr = &whyAmINotDoneYet;. En cambio: callback.dataPtr = (void*)val. Por otro lado, por supuesto obtienes void* y tener que devolverlo a int.

    – Francesco Dondi

    5 de abril de 2017 a las 12:09

Avatar de usuario de Drew Dormann
dibujó dormann

uintptr_t es un tipo entero sin signo que es capaz de almacenar un puntero de datos (no se especifica si puede contener un puntero de función). Lo que normalmente significa que tiene el mismo tamaño que un puntero.

Se define opcionalmente 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 enteros en un puntero, u ocultar el tipo de un puntero proporcionándolo como un “controlador” de entero.

  • Tenga en cuenta que 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 la 8086 (16 bits size_tpero 32 bits void*)

    – MSalters

    4 de diciembre de 2009 a las 9:08

  • para representar la diferencia entre dos punteros, tienes ptrdiff_t. uintptr_t no está hecho para eso.

    – jalf

    4 de diciembre de 2009 a las 14:44

  • @jalf: Por diferencia, sí, pero por distanciaquerrá un tipo sin firmar.

    – Drew Dorman

    4 de diciembre de 2009 a las 17:00

  • La definición de uintptr_t aparentemente no es obligatoria (por lo que no es estándar) ¡incluso en C++ 11! cplusplus.com/reference/cstdint (Obtuve la pista de la respuesta de Steve Jessop)

    – Antonio

    1 oct 2014 a las 20:56

  • @MarcusJ unsigned int por lo general no es lo suficientemente grande. Pero podría ser lo suficientemente grande. Este tipo existe específicamente para eliminar todos “asumiendo”.

    – Drew Dorman

    31 de mayo de 2015 a las 16:46


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 lanza a uintptr_t y manipularlo como un número entero habitual, luego devolverlo.

  • Por supuesto que podrías hacer eso, pero por supuesto sería un comportamiento indefinido. Creo que lo único que puede hacer con el resultado de un lanzamiento a uintptr_t es pasarlo sin cambios y devolverlo; todo lo demás es UB.

    – sleske

    13 de enero de 2011 a las 22:47

  • Hay momentos en los que necesita jugar con los bits, y normalmente generaría errores de compilación. Un ejemplo común es aplicar una memoria alineada de 16 bytes para ciertas aplicaciones críticas de video y rendimiento. stackoverflow.com/questions/227897/…

    – Nube

    22 de julio de 2014 a las 21:48

  • @sleske eso no es cierto. En las máquinas que tienen tipos autoalineados, los dos bits menos significativos de un puntero serán cero (porque las direcciones son múltiplos de 4 u 8). He visto programas que explotan esto para comprimir datos.

    – señor

    3 de enero de 2015 a las 17:48

  • @saadtaame: acabo de señalar que esto es UB de acuerdo con el estándar C. Eso no significa que no se pueda definir en algunos sistemas: los compiladores y los tiempos de ejecución son libres de definir un comportamiento específico para algo que es UB en C estándar. Así que no hay contradicción :-).

    – sleske

    3 de enero de 2015 a las 18:34

  • No es necesariamente exactamente del tamaño de un puntero. Todas las garantías estándar es que convertir un void* valor de puntero a uintptr_t y viceversa da como resultado un void* valor que se compara igual al puntero original. uintptr_t suele ser 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 de puntero a función convertido sin pérdida de información. Finalmente, no está garantizado que exista.

    –Keith Thompson

    02/09/2017 a las 20:28

Avatar de usuario de BGR
BGR

Ya hay muchas buenas respuestas a “¿qué es el tipo de datos uintptr_t?”. Intentaré abordar el tema “¿para qué se puede usar?” parte de 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, vea ¿Por qué no puede hacer 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 convertir punteros para escribir uintptr_t y luego realizar operaciones bit a bit.

Aquí hay un ejemplo de una función que acabo de escribir para hacer bit a bit exclusivo o de 2 punteros para almacenar en una lista enlazada XOR para que podamos atravesar en ambas direcciones como una lista doblemente enlazada pero sin la penalización 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));
  }

avatar de usuario de bd2357
bd2357

Corriendo el riesgo de obtener otra insignia de Nigromante, me gustaría agregar un muy buen uso para uintptr_t (o incluso intptr_t) y que está escribiendo código incrustado comprobable.

Principalmente escribo código incrustado dirigido a varios procesadores arm y actualmente tensilica. Estos tienen varios anchos de bus nativos y tensilica es en realidad una arquitectura de Harvard con códigos separados 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 cambios 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 direcciones, necesita un objeto generalizado para hacer matemáticas. Por lo tanto, la 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 que 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 sostener cualquiera de los dos.

— Editar —

Fue señalado por @chux, esto no es parte del estándar y las funciones no son objetos en C. Sin embargo, generalmente funciona y, dado que muchas personas ni siquiera conocen estos tipos, generalmente dejo un comentario explicando el truco. Otras búsquedas en SO en uintptr_t proporcionará una explicación más detallada. También hacemos cosas en pruebas unitarias que nunca haríamos en producción porque romper cosas es bueno.

  • No estoy seguro de si es relevante… ¿Has probado “definiciones de tipo opacas”? Véase: youtube.com/watch?v=jLdSjh8oqmE

    – shycha

    1 de abril de 2020 a las 0:24


  • @sleske Desearía que estuviera disponible en C. Pero tener stdint.h es mejor que nada. (También deseo enumeraciones donde el tipo sea más fuerte, pero la mayoría de los depuradores hacen un buen trabajo al resolverlos de cualquier manera)

    – bd2357

    13 mayo 2020 a las 23:31

  • “pero uintptr_t debería poder contener cualquiera”. –> No del todo. uintptr_t bueno para convertir a/desde un void * y void * punteros a/desde _objeto. Un puntero de función no puede viajar de ida y vuelta con un void *– (por ejemplo, puntero de función más grande que void *) y por extensión, no encajar en un uintptr_t.

    – chux – Reincorporar a Monica

    1 de julio de 2020 a las 17:43

  • @chux, sí, esto no es portátil en algunos casos limitados. Agregará una nota. Han pasado muchos años desde que miré cosas de Intel de 16 bits, pero los segmentos ensucian las cosas.

    – bd2357

    3 de julio de 2020 a las 18:22

  • No estoy seguro de si es relevante… ¿Has probado “definiciones de tipo opacas”? Véase: youtube.com/watch?v=jLdSjh8oqmE

    – shycha

    1 de abril de 2020 a las 0:24


  • @sleske Desearía que estuviera disponible en C. Pero tener stdint.h es mejor que nada. (También deseo enumeraciones donde el tipo sea más fuerte, pero la mayoría de los depuradores hacen un buen trabajo al resolverlos de cualquier manera)

    – bd2357

    13 mayo 2020 a las 23:31

  • “pero uintptr_t debería poder contener cualquiera”. –> No del todo. uintptr_t bueno para convertir a/desde un void * y void * punteros a/desde _objeto. Un puntero de función no puede viajar de ida y vuelta con un void *– (por ejemplo, puntero de función más grande que void *) y por extensión, no encajar en un uintptr_t.

    – chux – Reincorporar a Monica

    1 de julio de 2020 a las 17:43

  • @chux, sí, esto no es portátil en algunos casos limitados. Agregará una nota. Han pasado muchos años desde que miré cosas de Intel de 16 bits, pero los segmentos ensucian las cosas.

    – bd2357

    3 de julio de 2020 a las 18:22

¿Ha sido útil esta solución?