Domanda

Sto cercando di adattare un codice esistente a una macchina a 64 bit. Il problema principale è che in una funzione, il codificatore precedente utilizza un argomento void * che viene convertito in tipo adatto nella funzione stessa. Un breve esempio:

void function(MESSAGE_ID id, void* param)
{
    if(id == FOO) {
        int real_param = (int)param;
        // ...
    }
}

Ovviamente, su una macchina a 64 bit, viene visualizzato l'errore:

error: cast from 'void*' to 'int' loses precision

Vorrei correggerlo in modo che funzioni ancora su una macchina a 32 bit e nel modo più pulito possibile. Qualche idea?

È stato utile?

Soluzione

Usa intptr_t e uintptr_t.

Per assicurarti che sia definito in modo portatile, puoi usare un codice come questo:

#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

Inseriscilo in un file .h e includilo dove serve.

In alternativa, puoi scaricare la versione di Microsoft & # 8217; del file stdint.h da qui oppure utilizzane uno portatile da qui .

Altri suggerimenti

Direi che questo è il moderno modo C ++.

#include <cstdint>
void *p;
auto i = reinterpret_cast<std::uintptr_t>(p);

Modifica :

Il tipo corretto all'intero

quindi il modo giusto per memorizzare un puntatore come numero intero è usare i tipi uintptr_t o intptr_t. (Vedi anche in cppreference tipi interi per C99 ).

questi tipi sono definiti in <stdint.h> per C99 e nello spazio dei nomi std per C ++ 11 in <cstdint> (vedere tipi interi per C ++ ).

Versione C ++ 11 (e successive)

#include <cstdint>
std::uintptr_t i;

Versione C ++ 03

extern "C" {
#include <stdint.h>
}

uintptr_t i;

Versione C99

#include <stdint.h>
uintptr_t i;

L'operatore di casting corretto

In C c'è solo un cast e l'uso del cast C in C ++ è disapprovato (quindi non usarlo in C ++). In C ++ ci sono diversi cast. reinterpret_cast è il cast corretto per questa conversione (vedi anche qui ).

Versione C ++ 11

auto i = reinterpret_cast<std::uintptr_t>(p);

Versione C ++ 03

uintptr_t i = reinterpret_cast<uintptr_t>(p);

Versione C

uintptr_t i = (uintptr_t)p; // C Version

Domande correlate

'size_t' e 'ptrdiff_t' sono richiesti per abbinare la tua architettura (qualunque essa sia). Pertanto, penso che anziché usare "int", dovresti essere in grado di usare "size_t", che su un sistema a 64 bit dovrebbe essere di tipo 64 bit.

Questa discussione unsigned int vs size_t entra in qualche dettaglio in più.

Usa uintptr_t come tipo intero.

Diverse risposte hanno indicato uintptr_t e #include <stdint.h> come "la" soluzione. Cioè, suggerisco, parte della risposta, ma non l'intera risposta. Devi anche guardare dove viene chiamata la funzione con l'ID messaggio di FOO.

Considera questo codice e questa compilation:

$ 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
$

Noterai che c'è un problema nella posizione di chiamata (in main()) & # 8212; convertire un numero intero in un puntatore senza cast. Dovrai analizzare il tuo function() in tutti i suoi usi per vedere come gli vengono passati i valori. Il codice all'interno del mio void * funzionerebbe se le chiamate fossero scritte:

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

Dato che probabilmente i tuoi sono scritti in modo diverso, è necessario rivedere i punti in cui viene chiamata la funzione per assicurarsi che abbia senso utilizzare il valore come mostrato. Non dimenticare che potresti trovare un bug latente.

Inoltre, se hai intenzione di formattare il valore del parametro <inttypes.h> (come convertito), osserva attentamente l'intestazione stdint.h (anziché inttypes.h & # 8212; <stdint.h> fornisce i servizi di <=>, il che è insolito, ma lo standard C99 dice [t] che l'intestazione <=> include l'intestazione <=> e la estende con servizi aggiuntivi forniti da implementazioni ospitate ) e utilizzare le macro PRIxxx nelle stringhe di formato.

Inoltre, i miei commenti sono strettamente applicabili a C piuttosto che a C ++, ma il tuo codice è nel sottoinsieme di C ++ che è portatile tra C e C ++. Le possibilità sono buone o buone che i miei commenti siano validi.

  1. #include <stdint.h>
  2. Utilizza uintptr_t tipo standard definito nel file di intestazione standard incluso.

Penso che " che significa " di void * in questo caso è un handle generico. Non è un puntatore a un valore, è il valore stesso. (Questo è solo il modo in cui void * viene utilizzato dai programmatori C e C ++.)

Se contiene un valore intero, è meglio che rientri nell'intervallo intero!

Ecco un rendering semplice per intero:

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

Dovrebbe solo dare un avvertimento.

Mi sono imbattuto in questa domanda mentre studiavo il codice sorgente di SQLite .

Nel sqliteInt.h , lì è un paragrafo di codice definito una macro convertita tra intero e puntatore. L'autore ha fatto un'ottima dichiarazione prima sottolineando che dovrebbe essere un problema dipendente dal compilatore e quindi ha implementato la soluzione per tenere conto della maggior parte dei compilatori popolari là fuori.

#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

Ed ecco una citazione del commento per maggiori dettagli:

/*
** 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.
*/

Il credito va ai committer.

La cosa migliore da fare è evitare la conversione da tipo di puntatore a tipi di non puntatore. Tuttavia, questo chiaramente non è possibile nel tuo caso.

Come dicevano tutti, uintptr_t è ciò che dovresti usare.

Questo link ha buone informazioni sulla conversione in codice a 64 bit.

C'è anche una buona discussione di questo su comp. std.c

Poiché uintptr_t è non è garantito che ci sia in C ++ / C ++ 11 , se questo è uno modo di conversione puoi considerare uintmax_t, sempre definito in <cstdint> .

auto real_param = reinterpret_cast<uintmax_t>(param);

Per giocare in sicurezza, si potrebbe aggiungere un'asserzione ovunque nel codice:

static_assert(sizeof (uintmax_t) >= sizeof (void *) ,
              "No suitable integer type for conversion from pointer type");
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top