2009-02-02 15 views
13

¿Hay un tipo de entero con el mismo tamaño que el puntero? Garantizado en todas las microarquitecturas?sizeof (int) == sizeof (void *)?

+2

* * ¿Por qué quiere almacenar un puntero en un entero? No es claro para mí que sea una buena idea, incluso si se garantiza que funciona (lo cual no es así). – paxdiablo

+0

Optimización. Acceso único a la memoria/caché en lugar de dos accesos por separado. –

+3

Una buena razón para hacer esto parece ser la manipulación bit a bit. Los operadores bit a bit no parecen funcionar con punteros. Por ejemplo, si desea implementar una lista vinculada a XOR o si desea enmascarar algunos de los bits (para obtener una dirección de página), primero debe convertir a un número entero. –

Respuesta

4

Esto sería cierto en un sistema estándar de 32 bits, pero ciertamente no hay garantías, y podría encontrar muchas arquitecturas donde no es cierto. Por ejemplo, un error común es que sizeof (int) en x86_64 sería 8 (ya que es un sistema de 64 bits, supongo), que no lo es. En x86_64, sizeof (int) sigue siendo 4, pero sizeof (void *) es 8.

16

Según this Wikipedia page, en su C99 cabecera stdint.h fuerza intptr_t declarar y uintptr_t, pero luego de que, por supuesto, requiere

  • C99
  • Un implementador compilador que ha elegido para poner en práctica esta parte opcional del estándar

Así que, en general, creo que esto es difícil.

+0

buen punto sobre el typedefs. –

+0

GCC lo implementa; para Visual Studio, pruebe http://www.azillionmonkeys.com/qed/pstdint.h o http://code.google.com/p/msinttypes/ – Christoph

+0

También está la versión MinGW: http://cygwin.com /cgi-bin/cvsweb.cgi/src/winsup/mingw/include/stdint.h?rev=1.8&content-type=text/x-cvsweb-markup&cvsroot=src – Christoph

0

No creo que el estándar C, incluso especifica standard int sizes. Combine eso con todas las arquitecturas (8/16/32/64bit, etc.) y no hay forma de garantizar nada.

+0

Correcto, solo requiere sizeof (short) <= sizeof (int) <= sizeof (long), pero las garantías están ahí si se usa stdint.h proporcionó los límites – Friedrich

0

tipo de datos int sería la respuesta en la mayoría de las arquitecturas.

Pero thre es NO garantía de esto para arquitectura CUALQUIER (micro).

+1

Pero tenga en cuenta que 'int' en general es de 32 bits en x86-64, por lo que en algunos casos es posible que desee utilizar' long'. –

+0

El uso de int en una máquina de 64 bits sería incorrecto, excepto en el caso excepcional de ILP64. DEC Alpha fue el principal ejemplo de ILP64; No conozco ninguna arquitectura actual que use ILP64 en lugar de LP64 o (para Windows 64) LLP64. –

0

La respuesta parece ser "no", pero si todo lo que necesita es un tipo que puede actuar tanto, se puede utilizar una unión:

union int_ptr_t { 
    int i; 
    void* p; 
}; 
+0

Aunque debe tener cuidado con los problemas de alineación aquí. Por ejemplo, si void * es de 64 bits e int es de 32 bits, ¿qué 32 bits del vacío * se corresponden con el int? – Zooba

+1

Por supuesto, no hay garantía aquí de que pueda acceder a todos los bits del miembro del puntero a través del miembro entero. Eso es, con suerte, obvio, pero pensé que lo señalaría de todos modos. – unwind

+0

No. Quiero almacenar el puntero en un entero. –

1

No, lo más cerca que se llega a un portátil el tipo entero con capacidad de puntero sería intptr_t y ptrdiff_t.

14

En pocas palabras, no. No garantizado en todas las arquitecturas.

Mi pregunta es: ¿por qué? Si desea asignar un tipo lo suficientemente grande como para almacenar un void*, lo mejor que puede asignar es (sorprendentemente :-) un void*. ¿Por qué hay una necesidad de encajarlo dentro de un int?

EDITAR: Según sus comentarios a su pregunta duplicada, desea almacenar valores especiales del puntero (1,2,3) para indicar información adicional.

NO !! ¡No hagas esto!. No hay garantía de que 1, 2 y 3 no sean punteros perfectamente válidos. Ese puede ser el caso en los sistemas en los que se requiere alinear punteros en los límites de 4 bytes pero, como usted preguntó acerca de todas las arquitecturas, asumo que tiene portabilidad como un valor alto.

Encuentra otra manera de hacerlo correcto. Por ejemplo, utilice la unión (sintaxis de la memoria, puede estar equivocado):

typedef struct { 
    int isPointer; 
    union { 
     int intVal; 
     void *ptrVal; 
    } 
} myType; 

continuación, puede utilizar la isPointer 'booleano' para decidir si se debe tratar a la unión como un entero o un puntero.

EDIT:

Si la velocidad de ejecución es de primordial importancia, entonces la solución typedef es el camino a seguir. Básicamente, deberá definir el número entero que desea para cada plataforma en la que desea ejecutar. Puedes hacer esto con compilación condicional. También me gustaría añadir en una comprobación de tiempo de ejecución para asegurar que ha compilado para cada plataforma correctamente por lo tanto (estoy definiendo en la fuente, pero que pasaria eso como un indicador del compilador, como "cc -DPTRINT_INT"):

#include <stdio.h> 
#define PTRINT_SHORT 
#ifdef PTRINT_SHORT 
    typedef short ptrint; 
#endif 
#ifdef PTRINT_INT 
    typedef int ptrint; 
#endif 
#ifdef PTRINT_LONG 
    typedef long ptrint; 
#endif 
#ifdef PTRINT_LONGLONG 
    typedef long long ptrint; 
#endif 

int main(void) { 
    if (sizeof(ptrint) != sizeof(void*)) { 
     printf ("ERROR: ptrint doesn't match void* for this platform.\n"); 
     printf (" sizeof(void* ) = %d\n", sizeof(void*)); 
     printf (" sizeof(ptrint ) = %d\n", sizeof(ptrint)); 
     printf (" =================\n"); 
     printf (" sizeof(void* ) = %d\n", sizeof(void*)); 
     printf (" sizeof(short ) = %d\n", sizeof(short)); 
     printf (" sizeof(int  ) = %d\n", sizeof(int)); 
     printf (" sizeof(long ) = %d\n", sizeof(long)); 
     printf (" sizeof(long long) = %d\n", sizeof(long long)); 
     return 1; 
    } 

    /* rest of your code here */ 

    return 0; 
} 

En mi sistema (Ubuntu 8.04, 32-bit), me sale:

ERROR: ptrint typedef doesn't match void* for this platform. 
    sizeof(void* ) = 4 
    sizeof(ptrint ) = 2 
    ================= 
    sizeof(short ) = 2 
    sizeof(int  ) = 4 
    sizeof(long ) = 4 
    sizeof(long long) = 8 

En ese caso, me gustaría saber que necesitaba para compilar con PTRINT_INT (o largo). Puede haber una forma de detectar esto en tiempo de compilación con #if, pero no podría molestarme en investigarlo en este momento. Si golpeas una plataforma donde no hay un número entero suficiente para sostener un puntero, no tienes suerte.

Tenga en cuenta que el uso de valores de puntero especiales (1,2,3) para representar enteros también puede no funcionar en todas las plataformas; esto puede ser una dirección de memoria válida para punteros.

Aún así, si va a ignorar mi consejo, no hay mucho que pueda hacer para detenerlo. Es tu código después de todo :-). Una posibilidad es verificar todos los valores devueltos de malloc y, si obtiene 1, 2 o 3, solo vuelve a malloc (es decir, tiene un mymalloc() que hace esto automáticamente). Esta será una fuga de memoria menor, pero garantizará que no haya conflictos entre sus punteros especiales y punteros reales.

+0

El problema aquí es que debe verificar la memoria/línea de caché dos veces en la solución de unión (para isPointer y ptrVal). Este es el ciclo más interno del ciclo: nanosegundos hacen una * diferencia * real. –

+0

* Puede * almacenar toda la información necesaria en un campo de puntero, pero * solo * si descarta la portabilidad-preocupación. Caso especial su código si realmente necesita esto. –

+0

Ok, entendido. No puedo tener portabilidad y micro-optimización. Aún así, ¿alguien sabe de una plataforma donde malloc realmente puede devolver valores de 1, 2 o 3? –

13

El estándar C99 define tipos int estándar: tipos

7.18.1.4 Integer capaces de contener punteros de objeto El siguiente tipo designa un tipo entero con signo con la propiedad de que se puede convertir en cualquier puntero válido para anular este tipo, a continuación, se convierte de nuevo a puntero para anular, y el resultado será igual a comparar el puntero original:

intptr_t 

el siguiente tipo designa un tipo entero sin signo con la propiedad de que cualquier puntero válido para anular puede ser conver ted a este tipo, a continuación, se convierte de nuevo a puntero a void, y el resultado se compara igual a la original del puntero:

uintptr_t 

Estos tipos son opcionales.

C99 define también size_t y ptrdiff_t:

Los tipos son

ptrdiff_t 

que es el tipo entero con signo del resultado de restar dos punteros;

size_t 

que es el tipo entero sin signo del resultado del operador sizeof; y

Las arquitecturas que he visto tienen el tamaño máximo de un objeto igual a toda la memoria, por lo sizeof (size_t) == sizeof (void *), pero no estoy al tanto de todo lo que es a la vez portátil para C89 (que es size_t) y está garantizado que es lo suficientemente grande (que es uintptr_t).

+0

ptrdiff_t y size_t también están en C89, a diferencia de intptr_t y uintptr_t. –

+0

He agregado un lado para indicar qué tipos fallan en cada brazo del 'portátil para C89 y se garantiza que sea lo suficientemente grande' –

3

¿Qué pasa con C++. ¿Tengo que ir a las longitudes de:

template <int BITS> struct int_traits_t; 
template <> struct int_traits_t <16> {typedef uint16_t int_t;}; 
template <> struct int_traits_t <32> {typedef uint32_t int_t;}; 
template <> struct int_traits_t <64> {typedef uint64_t int_t;}; 
typedef int_traits_t <sizeof (void*) * 8>::int_t myintptr_t; 

sólo para obtener número entero portátil del tamaño de puntero?

+0

Si aplica sangría por cuatro espacios, su código se formateará como código. –

+0

esto también aumentaría un error de compilación si el tamaño del puntero no es uno de los especificados, lo que es bueno. – pqnet

0

Normalmente sizeof (* void) depende del ancho del bus de memoria (aunque no necesariamente - pre-RISC AS/400 tenía bus de direcciones de 48 bits pero punteros de 64 bits), e int generalmente es tan grande como el propósito general de la CPU registrarse (también hay excepciones: SGI C usa entradas de 32 bits en MIPS de 64 bits).

Por lo tanto, no hay ninguna garantía.

2

La solución estándar a este problema es escribir un pequeño programa que comprueba los tamaños de todos los tipos int (short int, int, long int) y los compara con void *. Si hay una coincidencia, emite un fragmento de código que define el tipo de intptr. Puede poner esto en un archivo de cabecera y usar el nuevo tipo.

Es muy sencillo para incluir este código en el proceso de construcción (usando make, por ejemplo)