c++ - que - Convertir un puntero en un entero
punteros y arreglos en c (10)
-
#include <stdint.h>
- Use
uintptr_t
tipo estándar definido en el archivo de encabezado estándar incluido.
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 en un 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, obtengo el error:
error: cast from ''void*'' to ''int'' loses precision
Me gustaría corregir esto para que funcione en una máquina de 32 bits y lo más limpiamente posible. Alguna idea ?
''size_t'' y ''ptrdiff_t'' son necesarios para coincidir con su arquitectura (cualquiera 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 de 64 bits.
Esta discusión unsigned int vs size_t entra en un poco más de detalle.
Como uintptr_t
no está garantizado en C ++ / C ++ 11 , si se trata de una conversión unidireccional puede considerar uintmax_t
, siempre definido en <cstdint>
.
auto real_param = reinterpret_cast<uintmax_t>(param);
Para jugar seguro, uno podría agregar en cualquier parte del código una afirmación:
static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
"No suitable integer type for conversion from pointer type");
Creo que el "significado" de void * en este caso es un identificador genérico. No es un puntero a un valor, es el valor en sí mismo. (Esto simplemente sucede que los programadores C y C ++ utilizan el vacío *).
Si mantiene un valor entero, ¡sería mejor que estuviera dentro del rango entero!
Aquí está el renderizado fácil a entero:
int x = (char*)p - (char*)0;
Solo debería dar una advertencia.
Lo mejor que puede hacer es evitar convertir tipos de puntero a tipos que no sean punteros. Sin embargo, esto claramente no es posible en su caso.
Como todos dijeron, el uintptr_t es lo que debes usar.
Este link tiene buena información sobre la conversión a código de 64 bits.
También hay una buena discusión de esto en comp.std.c
Me encontré con esta pregunta mientras estudiaba el código fuente de SQLite .
En sqliteInt.h , hay un párrafo de código definido como macro convert 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 va a los committers.
Use intptr_t
y uintptr_t
.
Para asegurarse de que esté definido de manera portátil, puede usar 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
Solo colóquelo en algún archivo .h e inclúyalo donde lo necesite.
Alternativamente, puede descargar la versión de Microsoft del archivo stdint.h
desde here o usar una portátil desde here .
Use uintptr_t
como su tipo de entero.
Varias respuestas apuntan a uintptr_t
y #include <stdint.h>
como ''la'' solución. Es decir, sugiero, parte de la respuesta, pero no toda la respuesta. También debe ver dónde se llama a la función con la ID del 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()
), que convierte un entero en un puntero sin conversión. Necesitará analizar su function()
en todos sus usos para ver cómo se le pasan los valores. El código dentro de mi function()
funcionaría si las llamadas se escribieran:
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 olvides que puedes encontrar un error latente.
Además, si va a formatear el valor del parámetro void *
(como convertido), observe cuidadosamente el <inttypes.h>
(en lugar de stdint.h
- inttypes.h
proporciona los servicios de stdint.h
, que es inusual, pero el estándar C99 dice que [t] el encabezado <inttypes.h>
incluye el encabezado <stdint.h>
y lo amplía con facilidades adicionales provistas por implementaciones alojadas ) y usa las macros PRIxxx en las cadenas de formato.
Además, mis comentarios son estrictamente aplicables a C en lugar de a C ++, pero su código está en el subconjunto de C ++ que es portátil entre C y C ++. Las posibilidades son buenas o buenas de que mis comentarios se apliquen.
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
así que la forma correcta de almacenar un puntero como un entero es usar los tipos uintptr_t
o intptr_t
. (Ver también en cppreference integeger types para C99 ).
estos tipos se definen en <stdint.h>
para C99 y en el espacio de nombres std
para C ++ 11 en <cstdint>
(vea los tipos de enteros para C ++ ).
Versión de C ++ 11 (y en adelante)
#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 casting correcto
En C solo hay un molde y usar el molde C en C ++ está mal visto (así que no lo use en C ++). En C ++ hay diferentes moldes. reinterpret_cast
es el modelo correcto para esta conversión (Ver también here ).
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