Estoy tratando de adaptar un código existente a una máquina de 64 bits. El principal problema es que en una función, el codificador anterior usa un argumento void * que se convierte al tipo adecuado en la función misma. Un pequeño ejemplo:
void function(MESSAGE_ID id, void* param)
{
if(id == FOO) {
int real_param = (int)param;
// ...
}
}
Por supuesto, en una máquina de 64 bits, aparece el error:
error: cast from 'void*' to 'int' loses precision
Me gustaría corregir esto para que todavía funcione en una máquina de 32 bits y lo más limpiamente posible. Alguna idea ?
size_t
no funciona es la memoria segmentada i386. Aunque una máquina de 32 bits,sizeof
devuelve2
asize_t
. La respuesta de Alex a continuación parece correcta. Alex responde yuintptr_t
funciona en casi todas partes y ahora es estándar. Proporciona un tratamiento de C ++ 11 e incluso proporciona protecciones de encabezado de C ++ 03.Respuestas:
Utilice
intptr_t
yuintptr_t
.Para asegurarse de que esté definido de forma portátil, puede utilizar un código como este:
#if defined(__BORLANDC__) typedef unsigned char uint8_t; typedef __int64 int64_t; typedef unsigned long uintptr_t; #elif defined(_MSC_VER) typedef unsigned char uint8_t; typedef __int64 int64_t; #else #include <stdint.h> #endif
Simplemente colóquelo en algún archivo .hy inclúyalo donde lo necesite.
Alternativamente, puede descargar la versión de Microsoft del
stdint.h
archivo desde aquí o usar una portátil desde aquí .fuente
<cstdint>
o descargar el archivo apropiadocstdint
si descarga un archivostdint.h
.cstdint
es parte del estándar C ++, al igual que todos los nombres de tipo definidos allí. De hecho, es apropiado para las etiquetas especificadas. ... No estoy de acuerdo con definir los tipos manualmente, pero puede ser necesario cuando se trabaja con compiladores que no lo hacen.Yo diría que esta es la forma moderna de C ++.
#include <cstdint> void *p; auto i = reinterpret_cast<std::uintptr_t>(p);
EDITAR :
El tipo correcto para el entero
por lo que la forma correcta de almacenar un puntero como un entero es usar los tipos
uintptr_t
ointptr_t
. (Ver también en cppreference tipos de enteros para C99 ).estos tipos se definen en
<stdint.h>
para C99 y en el espaciostd
de nombres para C ++ 11 en<cstdint>
(consulte los tipos de enteros para C ++ ).Versión C ++ 11 (y posteriores)
#include <cstdint> std::uintptr_t i;
Versión C ++ 03
extern "C" { #include <stdint.h> } uintptr_t i;
Versión C99
#include <stdint.h> uintptr_t i;
El operador de fundición correcto
En C solo hay un elenco y el uso del elenco de C en C ++ está mal visto (así que no lo use en C ++). En C ++ hay diferentes moldes.
reinterpret_cast
es el elenco correcto para esta conversión (ver también aquí ).Versión C ++ 11
auto i = reinterpret_cast<std::uintptr_t>(p);
Versión C ++ 03
uintptr_t i = reinterpret_cast<uintptr_t>(p);
Versión C
uintptr_t i = (uintptr_t)p; // C Version
preguntas relacionadas
fuente
uintptr_t
lugar desize_t
, ¿por qué lo requierereinterpret_cast
? Parece algo sencillostatic_cast
, ya que el estándar proporciona específicamente los tipos de datos compatibles ...static_cast
podría convertir el tipo o si es un puntero podría hacer ajustes de puntero si el tipo lo necesita.reinterpret_cast
en realidad, solo está cambiando el tipo de patrón de memoria subyacente (sin mutaciones). para aclarar:static_cast
se comporta idéntico aquí.'size_t' y 'ptrdiff_t' son necesarios para coincidir con su arquitectura (sea la que sea). Por lo tanto, creo que en lugar de usar 'int', debería poder usar 'size_t', que en un sistema de 64 bits debería ser del tipo de 64 bits.
Esta discusión unsigned int vs size_t entra en un poco más de detalle.
fuente
size_t
es que debe contener el resultado de cualquierasizeof()
. Esto no necesariamente lo convierte en 64 bits en x64. ver tambiénsize_t
puede almacenar de forma segura el valor de un puntero que no es miembro. Consulte en.cppreference.com/w/cpp/types/size_t .Úselo
uintptr_t
como su tipo de entero.fuente
Varias respuestas han señalado
uintptr_t
y#include <stdint.h>
como "la" solución. Eso es, sugiero, parte de la respuesta, pero no toda la respuesta. También debe observar dónde se llama a la función con el ID de mensaje de FOO.Considere este código y compilación:
$ cat kk.c #include <stdio.h> static void function(int n, void *p) { unsigned long z = *(unsigned long *)p; printf("%d - %lu\n", n, z); } int main(void) { function(1, 2); return(0); } $ rmk kk gcc -m64 -g -O -std=c99 -pedantic -Wall -Wshadow -Wpointer-arith \ -Wcast-qual -Wstrict-prototypes -Wmissing-prototypes \ -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE kk.c -o kk kk.c: In function 'main': kk.c:10: warning: passing argument 2 of 'func' makes pointer from integer without a cast $
Observará que hay un problema en la ubicación de la llamada (en
main()
): convertir un entero en un puntero sin conversión. Vas a necesitar analizar tufunction()
en todos sus usos para ver cómo se le pasan los valores. El código dentro de mifunction()
funcionaría si las llamadas estuvieran escritas:unsigned long i = 0x2341; function(1, &i);
Dado que el suyo probablemente esté escrito de manera diferente, debe revisar los puntos donde se llama a la función para asegurarse de que tenga sentido usar el valor como se muestra. No lo olvide, puede estar encontrando un error latente.
Además, si va a formatear el valor del
void *
parámetro (como convertido), mire detenidamente el<inttypes.h>
encabezado (en lugar destdint.h
-inttypes.h
proporciona los servicios destdint.h
, lo cual es inusual, pero el estándar C99 dice que [t] el encabezado<inttypes.h>
incluye el encabezado<stdint.h>
y lo extiende con facilidades adicionales proporcionadas por implementaciones alojadas ) y use las macros PRIxxx en sus cadenas de formato.Además, mis comentarios son estrictamente aplicables a C en lugar de C ++, pero su código está en el subconjunto de C ++ que es portátil entre C y C ++. Las posibilidades de que se apliquen mis comentarios son justas a buenas.
fuente
#include <stdint.h>
uintptr_t
el tipo estándar definido en el archivo de encabezado estándar incluido.fuente
Me encontré con esta pregunta mientras estudiaba el código fuente de SQLite .
En sqliteInt.h , hay un párrafo de código que define una conversión macro entre entero y puntero. El autor hizo una muy buena declaración primero señalando que debería ser un problema dependiente del compilador y luego implementó la solución para dar cuenta de la mayoría de los compiladores populares que existen.
#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ # define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X)) #elif !defined(__GNUC__) /* Works for compilers other than LLVM */ # define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X]) # define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) #elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */ # define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X)) #else /* Generates a warning - but it always works */ # define SQLITE_INT_TO_PTR(X) ((void*)(X)) # define SQLITE_PTR_TO_INT(X) ((int)(X)) #endif
Y aquí hay una cita del comentario para más detalles:
/* ** The following macros are used to cast pointers to integers and ** integers to pointers. The way you do this varies from one compiler ** to the next, so we have developed the following set of #if statements ** to generate appropriate macros for a wide range of compilers. ** ** The correct "ANSI" way to do this is to use the intptr_t type. ** Unfortunately, that typedef is not available on all compilers, or ** if it is available, it requires an #include of specific headers ** that vary from one machine to the next. ** ** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on ** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)). ** So we have to define the macros in different ways depending on the ** compiler. */
El crédito es para los comprometidos.
fuente
Lo mejor que puede hacer es evitar la conversión de tipos de puntero a tipos que no son de puntero. Sin embargo, esto claramente no es posible en su caso.
Como todos dijeron, el uintptr_t es lo que debe usar.
Este enlace tiene buena información sobre la conversión a código de 64 bits.
También hay una buena discusión sobre esto en comp.std.c
fuente
Creo que el "significado" de void * en este caso es un identificador genérico. No es un puntero a un valor, es el valor mismo. (Esta es la forma en que los programadores de C y C ++ usan void *).
Si tiene un valor entero, ¡es mejor que esté dentro del rango de números enteros!
Aquí hay una representación fácil a un número entero:
int x = (char*)p - (char*)0;
Solo debería dar una advertencia.
fuente
Dado
uintptr_t
que no se garantiza que esté allí en C ++ / C ++ 11 , si se trata de una conversión unidireccional, puede consideraruintmax_t
, siempre definida en<cstdint>
.auto real_param = reinterpret_cast<uintmax_t>(param);
Para jugar seguro, uno podría agregar en cualquier lugar del código una afirmación:
static_assert(sizeof (uintmax_t) >= sizeof (void *) , "No suitable integer type for conversion from pointer type");
fuente