tamaño de (int) == tamaño de (vacío*)?

8 minutos de lectura

¿Hay un tipo entero con el mismo tamaño que el puntero? ¿Garantizado en todas las microarquitecturas?

  • Por qué ¿Quieres almacenar un puntero en un número entero? No me queda claro que esta sea una buena idea, incluso si estuviera garantizado que funcionara (que no lo es).

    – pax diablo

    2 de febrero de 2009 a las 11:06

  • Mejoramiento. Acceso único a la memoria/caché en lugar de dos accesos separados.

    martín

    2 de febrero de 2009 a las 11:30

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

    – Jay Conrod

    4 de mayo de 2009 a las 22:06

De acuerdo a esta página de wikipediaen C99 su encabezado stdint.h puede que declarar intptr_t y uintptr_t, pero eso, por supuesto, requiere

  • C99
  • Un implementador del compilador que ha elegido implementar esta parte opcional del estándar

Entonces, en general, creo que este es difícil.

  • GCC lo implementa; para Visual Studio, intente azillionmonkeys.com/qed/pstdint.h o code.google.com/p/msinttypes

    – Cristóbal

    2 de febrero de 2009 a las 11:03

  • También está la versión MinGW: cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/mingw/include/…

    – Cristóbal

    2 de febrero de 2009 a las 11:16

  • los estándar lo hace NO garantizar que son del mismo tamaño; solo que puedes lanzar a (u)intptr_t y viceversa (siempre que la memoria a la que hace referencia siga siendo válida), y que un puntero nulo se convierta en 0. En arquitecturas “sensatas”, solo será una dirección de memoria, pero esto no está garantizado de ninguna manera (por ejemplo, la conversión podría aleatoriamente permutar los bits, no hay garantía de que pueda inferir la alineación de la uintptr_t representación, la aritmética de punteros no necesita funcionar…).

    – tc.

    11 de junio de 2012 a las 17:32

  • @tc. ¿Desde cuándo el estándar C garantiza que un puntero nulo se convierta en un entero cero?

    – Deduplicador

    24 de agosto de 2020 a las 23:12

  • Huh, de hecho no lo hace. C89/99 solo parece garantizar que convertir una “expresión constante entera con el valor 0” en un puntero da un puntero nulo, por ejemplo int x = 0; if ((void*)x) { ... } está definida por la implementación. Sin embargo, sería realmente extraño si el comportamiento cambiara drásticamente en el momento en que el compilador decidiera que algo ya no era constante.

    – tc.

    19 de junio de 2021 a las 20:28


avatar de usuario
paxdiablo

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

Mi pregunta es: ¿por qué? Si desea asignar un tipo lo suficientemente grande para almacenar un void*lo mejor para asignar es (sorprendentemente 🙂 un void*. ¿Por qué es necesario 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 debe alinear los punteros en los límites de 4 bytes pero, dado que preguntó sobre todas las arquitecturas, asumo que tiene la portabilidad como un valor alto.

Encuentra otra forma de hacerlo que sea correcta. Por ejemplo, use la unión (sintaxis de memoria, puede ser incorrecta):

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

Luego, puede usar isPointer ‘booleano’ para decidir si debe tratar la unión como un número entero o un puntero.

EDITAR:

Si la velocidad de ejecución es de suma 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 la compilación condicional. También agregaría una verificación de tiempo de ejecución para asegurarme de que haya compilado para cada plataforma correctamente (lo estoy definiendo en el código fuente, pero lo pasaría 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 bits), obtengo:

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, sabría que necesitaba 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 golpea una plataforma donde no hay un tipo entero suficiente para sostener un puntero, no tiene suerte.

Tenga en cuenta que es posible que el uso de valores de puntero especiales (1,2,3) para representar números enteros no funcione en todas las plataformas; en realidad, estas pueden ser direcciones de memoria válidas para punteros.

Aún así, si vas a ignorar mi consejo, no hay mucho que pueda hacer para detenerte. Es su código después de todo :-). Una posibilidad es comprobar todos los valores de retorno de malloc y, si obtiene 1, 2 o 3, simplemente malloc de nuevo (es decir, tener un mymalloc() que hace esto automáticamente). Esta será una pérdida de memoria menor, pero garantizará que no haya conflictos entre sus punteros especiales y punteros reales.

  • El problema aquí es que debe verificar la memoria/cacheline dos veces en la solución de unión (para isPointer y ptrVal). Este es el ciclo más interno del ciclo: los nanosegundos hacen un verdadero diferencia.

    martín

    2 de febrero de 2009 a las 11:42

  • lata almacenar toda la información necesaria en un campo de puntero, pero solamente si descarta la preocupación por la portabilidad. Caso especial su código si realmente lo necesita.

    – Magnus Hoff

    2 de febrero de 2009 a las 11:48

  • Entendido. No puedo tener tanto portabilidad como microoptimización. Aún así, ¿alguien conoce una plataforma en la que malloc pueda devolver valores de 1, 2 o 3?

    martín

    2 de febrero de 2009 a las 11:54

  • Ver actualizaciones para responder, Martin. Si realmente quiere evitar conflictos, codifique su propio mymalloc() que no pasa por esas direcciones. Es una chapuza, pero debería funcionar.

    – pax diablo

    2 de febrero de 2009 a las 11:58

  • Paz, gracias por la respuesta. Sin embargo, permítanme sugerir una forma diferente de resolver el problema: unsigned char *dummy = malloc (4); vacío *valor_especial1 = ficticio + 0; …. Esto mantendría los valores especiales en el caché L1d en lugar de en L1i, pero creo que puedo hacerlo.

    martín

    2 de febrero de 2009 a las 12:11

avatar de usuario
Pete Kirkham

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

7.18.1.4 Tipos enteros capaces de contener punteros de objeto El siguiente tipo designa un tipo entero con signo con la propiedad de que cualquier puntero válido para anular puede convertirse a este tipo, luego volver a convertirse en puntero para anular, y el resultado se comparará igual al puntero original:

    intptr_t

El siguiente tipo designa 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:

   uintptr_t

Estos tipos son opcionales.

C99 también define 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 que sizeof(size_t) == sizeof(void*), pero no conozco nada que sea portátil a C89 (que size_t es) y garantizado que sea lo suficientemente grande (que uintptr_t es ).

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

    –Jonathan Leffler

    3 de febrero de 2009 a las 4:44

  • He agregado un aparte para indicar qué tipos fallan en cada brazo del ‘ambos portátiles para C89 y garantizados para ser lo suficientemente grandes’

    –Pete Kirkham

    3 de febrero de 2009 a las 8:30

avatar de usuario
hombre de mentira

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 concepto erróneo común es que sizeof(int) en x86_64 sería 8 (ya que es un sistema de 64 bits, supongo), lo cual no es. En x86_64, sizeof(int) sigue siendo 4, pero sizeof(void*) es 8.

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

Es simple incluir este código en el proceso de construcción (usando makepor ejemplo)

avatar de usuario
ronny vindenes

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

avatar de usuario
Rob McCready

No.

No creo que el estándar C incluso especifique tamaños int estándar. Combine eso con todas las arquitecturas que existen (8/16/32/64 bits, etc.) y no hay forma de garantizar nada.

  • Así es, solo requiere sizeof (short) <= sizeof (int) <= sizeof (long), pero las garantías están ahí si uno usa los límites proporcionados por stdint.h

    – Federico

    2 de febrero de 2009 a las 12:32

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad