2012-02-29 19 views
21

¿Es una buena idea usar intptr_t como almacenamiento de uso general (para contener punteros y valores enteros) en lugar de void*? (Como se ve aquí: http://www.crystalspace3d.org/docs/online/manual/Api1_005f0-64_002dBit-Portability-Changes.html)¿Usar intptr_t en lugar de void *?

Por lo que ya he leído:

  • int ->void* ->int ida y vuelta no está garantizada para mantener valor original; Supongo int ->intptr_t ->int hará
  • aritmética de punteros tanto en void* y intptr_t requieren moldes, por lo que ninguno obtiene ventaja aquí
  • void* significa moldes menos explícitas al almacenar punteros, intptr_t significa menos moldes al almacenar valores enteros
  • intptr_t requiere C99

¿Qué más debo tener en cuenta?

+0

No. (Si lo fuera, entonces solo habrían agregado la semántica de intptr_t a 'void *') –

+0

La publicación pregunta "(para contener punteros y valores enteros)", pero luego solo se trata 'int',' void * 'y' intptr_t'. Como 'uintmax_t',' size_t', 'long long', etc. también son tipos enteros, suena como que la pregunta es solo sobre punteros de objeto, tipos' intptr_t' y 'int'. – chux

Respuesta

25

¿Es una buena idea usar intptr_t como dispositivo de almacenamiento de propósito general (para mantener los punteros y valores enteros) en lugar de void*?

intptr_t no está garantizado de existir. Primero, como nota, fue introducido en C99. En segundo lugar, no se requiere que las implementaciones tengan un tipo de entero lo suficientemente grande como para contener los valores del puntero convertido sin pérdida de información.

La conversión de una int a intptr_t y vuelta es poco probable perder la información, pero no hay garantía real que intptr_t es más ancha que int.

Si desea almacenar valores de puntero, guárdelos en objetos de puntero. Para eso están los objetos del puntero.

Cualquier puntero a un objeto o tipo incompleto se puede convertir a void* y viceversa sin pérdida de información. No existe tal garantía para los punteros a las funciones, pero cualquier tipo de puntero a función se puede convertir a cualquier otro tipo de puntero a función y viceversa sin pérdida de información. (Me refiero al estándar C; creo que POSIX proporciona algunas garantías adicionales.)

Si desea almacenar un entero o un valor de puntero en el mismo objeto, lo primero que debe hacer es volver a pensar tu diseño. Si ya lo ha hecho y ha llegado a la conclusión de que realmente desea hacerlo, considere usar una unión (y lleve un registro detallado de qué tipo de valor ha almacenado más recientemente).

Hay API que utilizan un argumento void* para permitir que se pasen datos arbitrarios; ver, por ejemplo, la función POSIX pthread_create(). Se puede abusar de esto lanzando un valor entero a void* pero es más seguro pasar la dirección de un objeto entero.

+2

Amplíe "_unlikely_ para perder información". 'intptr_t' hace ida y vuelta un puntero a uno que se compara _equally_ - es ese patrón potencialmente no el mismo bit la pérdida de información de tu comentario. – chux

+2

@chux: Como dije, no hay garantía de que 'intptr_t' sea más ancho que' int' (aunque no conozco ninguna implementación donde no lo esté). Si, por ejemplo, 'intptr_t' es de 32 bits y' int' es de 64 bits, entonces la conversión de 'int' a' intptr_t' y de vuelta a 'int' podría perder información. Específicamente, si el valor 'int' está fuera del rango de' intptr_t', la primera conversión produce un resultado definido por la implementación. –

+2

Lo siento, lea la respuesta incorrectamente, ya que "Convertir un _pointer_ a' intptr_t' y volver es poco probable que pierda información ... ". Mi comentario estuvo basado en ese pensamiento. De acuerdo sobre la rareza de los problemas 'int/intptr_t'. – chux

5

No, no se puede garantizar que un tipo en particular sea una forma razonable de almacenar punteros y enteros, y además, hace que el código sea confuso. Hay una mejor manera.

Si desea almacenar un entero y un puntero en el mismo objeto, el método limpio y portátil es el uso de un sindicato:

union foo { 
    int integer_foo; 
    void *pointer_foo; 
} 

Esta es portátil y le permite almacenar ambos tipos de cosas en el tamaño de almacenamiento necesario para el mayor de los dos. Se garantiza que siempre funciona.

+1

Do 'int' y' void * 'tienen una subsecuencia inicial común en términos de diseño? De lo contrario, esto no es portátil, no está permitido almacenar ambos miembros a la vez y esto no garantiza que "siempre funcione". –

+3

Lo que es seguro es que falta un ';' después de la definición de unión. – Shoe

+1

@LightnessRacesinOrbit No creo que afirmara que se pueden almacenar dos valores al mismo tiempo. "both" significa que se puede almacenar un 'int' y se puede almacenar un' void * ', pero no hay ninguna sugerencia de simultaneidad. NÓTESE BIEN. No importaría si 'int' y' void * 'tuvieran una secuencia inicial común, ya que esa regla solo se aplica a las estructuras. –

Cuestiones relacionadas