Pregunta

Estoy intentando adaptar un código existente a una máquina de 64 bits.El principal problema es que en una función, el codificador anterior utiliza un argumento void* que se convierte al tipo adecuado en la propia función.Un breve 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 siga funcionando en una máquina de 32 bits y de la forma más limpia posible.Alguna idea ?

¿Fue útil?

Solución

Usar intptr_t y uintptr_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 .h e inclúyalo donde lo necesite.

Alternativamente, puede descargar la versión de Microsoft del stdint.h archivo de aquí o utilizar uno portátil de aquí.

Otros consejos

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 o intptr_t. (Consulte también en cppreference tipos enteros para C99 ).

estos tipos se definen en <stdint.h> para C99 y en el espacio de nombres std para C ++ 11 en <cstdint> (consulte tipos enteros para C ++ ).

C ++ 11 (y versiones 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 casting correcto

En C solo hay un molde y usar el molde C en C ++ está mal visto (así que no lo uses en C ++). En C ++ hay diferentes modelos. reinterpret_cast es el reparto correcto para esta conversión (consulte 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

'size_t' y 'ptrdiff_t' son necesarios para que coincida con su arquitectura (sea cual 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 un tipo de 64 bits.

Esta discusión unsigned int vs size_t entra en un poco más de detalle.

Use uintptr_t como su tipo de entero.

Varias respuestas han apuntado a uintptr_t y #include <stdint.h> como 'la' solución.Sugiero que esto es parte de la respuesta, pero no la respuesta completa.También debe mirar dónde se llama la función con el ID del mensaje 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 el lugar de llamada (en main()) - convertir un número entero en un puntero sin conversión.Vas a necesitar analizar tu 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 fueran escritas:

unsigned long i = 0x2341;
function(1, &i);

Dado que los suyos probablemente estén escritos 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 olvides, es posible que estés encontrando un error latente.

Además, si va a formatear el valor del void * parámetro (como se convirtió), mire cuidadosamente el <inttypes.h> encabezado (en lugar de stdint.hinttypes.h proporciona los servicios de stdint.h, lo cual es inusual, pero el estándar C99 dice [el] encabezado <inttypes.h> incluye el encabezado <stdint.h> y lo extiende con instalaciones adicionales proporcionadas por implementaciones alojadas) y utilice las macros PRIxxx en sus 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 de que mis comentarios se apliquen son de medianas a buenas.

  1. #include <stdint.h>
  2. Utilice uintptr_t tipo estándar definido en el archivo de encabezado estándar incluido.

Creo que el " que significa " de vacío * en este caso es un identificador genérico. No es un puntero a un valor, es el valor en sí mismo. (Esto simplemente es cómo void * es utilizado por los programadores de C y C ++).

¡Si tiene un valor entero, será mejor que esté dentro del rango entero!

Aquí es fácil renderizar a entero:

int x = (char*)p - (char*)0;

Solo debería dar una advertencia.

Me encontré con esta pregunta al estudiar el código fuente de SQLite .

En el sqliteInt.h , hay es un párrafo de código definido como una macro conversión entre entero y puntero. El autor hizo una muy buena declaración al señalar primero 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 encargados.

Lo mejor que puede hacer es evitar la conversión del tipo de puntero a tipos sin puntero. Sin embargo, esto claramente no es posible en su caso.

Como todos decían, el uintptr_t es lo que debes 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

Dado que uintptr_t es no se garantiza que esté allí en C ++ / C ++ 11 , si este es uno conversión de forma que puede considerar uintmax_t, siempre definida en <cstdint> .

auto real_param = reinterpret_cast<uintmax_t>(param);

Para jugar con seguridad, se podría agregar una afirmación en cualquier parte del código:

static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
              "No suitable integer type for conversion from pointer type");
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top