c - pointer - sizeof(int)== sizeof(void*)?
pointers in c pdf (11)
De acuerdo con esta página de Wikipedia , en C99 su encabezado stdint.h podría declarar intptr_t y uintptr_t, pero eso por supuesto requiere
- C99
- Un implementador de compilador que ha elegido implementar esta parte opcional de la norma
Entonces, en general, creo que este es difícil.
¿Hay un tipo de entero con el mismo tamaño que el puntero? Garantizado en todas las microarquitecturas?
El tipo de datos int sería la respuesta en la mayoría de las arquitecturas.
Pero esto NO es garantía para NINGUNA (micro) arquitectura.
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 sea 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.
La respuesta parece ser "no", pero si todo lo que necesita es un tipo que pueda actuar como ambos, puede usar una unión:
union int_ptr_t {
int i;
void* p;
};
La solución estándar para 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 simple incluir este código en el proceso de compilación (usando make
, por ejemplo)
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
.
No.
No creo que el estándar C siquiera especifique los tamaños int estándar . Combine eso con todas las arquitecturas (8/16/32 / 64bit, etc.) y no hay forma de garantizar nada.
Por lo general, 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 registro de propósito general de la CPU (hay también excepciones: SGI C utilizó entradas de 32 bits en MIPS de 64 bits).
Entonces no hay garantía.
El estándar C99 define tipos int estándar:
7.18.1.4 Tipos enteros capaces de contener punteros a objetos El siguiente tipo designa un tipo entero con signo con la propiedad de que cualquier puntero válido a void se puede convertir a este tipo, luego se convierte de nuevo a puntero en void, y el resultado se comparará igual que el puntero original:
intptr_t
El siguiente tipo designa un tipo de entero sin signo con la propiedad de que cualquier apuntador válido a void se puede convertir a este tipo, luego se convierte de nuevo a puntero en void, y el resultado se comparará 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 la memoria completa, por lo que sizeof (size_t) == sizeof (void *), pero no conozco nada que sea portátil para C89 (que size_t
es ) y garantizado para ser lo suficientemente grande (que uintptr_t
es).
Simplemente, 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 caber dentro de un int
?
EDITAR: en función de 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.
Encuentre otra forma de hacerlo correcta. Por ejemplo, use la unión (la sintaxis de la memoria, puede ser incorrecta):
typedef struct {
int isPointer;
union {
int intVal;
void *ptrVal;
}
} myType;
Luego puede usar isPointer ''boolean'' para decidir si debe tratar la unión como un entero o un puntero.
EDITAR:
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 agregaría un control en tiempo de ejecución para asegurarme de que compiló correctamente para cada plataforma (lo estoy definiendo en la 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 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 los 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, simplemente malloc nuevamente (es decir, tener un mymalloc () que lo hace automáticamente). Esta será una fuga de memoria menor, pero garantizará que no haya conflictos entre sus punteros especiales y punteros reales.
¿Y qué hay de C ++? ¿Tengo que llegar a lo siguiente:
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;
solo para obtener un entero portátil del tamaño del puntero?