2009-02-21 26 views
331

Sono poco confuso con l'applicabilità di reinterpret_cast rispetto a static_cast. Da quello che ho letto le regole generali sono quelle di usare il cast statico quando i tipi possono essere interpretati in fase di compilazione da cui la parola static. Questo è il cast che il compilatore C++ usa internamente anche per i cast impliciti.Quando usare reinterpret_cast?

reinterpret_cast s sono applicabili in due scenari, convertire tipi di interi in tipi di puntatori e viceversa o convertire un tipo di puntatore in un altro. L'idea generale che ottengo è che questo non è commovibile e dovrebbe essere evitato.

Dove sono un po 'confuso è un utilizzo di cui ho bisogno, chiamo C++ da C e il codice C deve rimanere sull'oggetto C++ quindi in pratica contiene uno void*. Quale cast dovrebbe essere usato per convertire tra il void * e il tipo di Classe?

Ho visto l'utilizzo di entrambi static_cast e reinterpret_cast? Anche se da quello che sto leggendo sembra static è meglio come il cast può accadere in fase di compilazione? Anche se dice di usare reinterpret_cast per convertire da un tipo di puntatore a un altro?

+2

'reinterpret_cast' non si verifica in fase di esecuzione. Sono entrambe istruzioni in fase di compilazione. Da http://en.cppreference.com/w/cpp/language/reinterpret_cast: "A differenza di static_cast, ma come const_cast, l'espressione reinterpret_cast non si compila con le istruzioni della CPU, è puramente una direttiva del compilatore che istruisce il compilatore per trattare la sequenza di bit (rappresentazione dell'oggetto) di espressione come se avesse il tipo new_type. " –

+0

@HeretoLearn, è possibile aggiungere i pezzi di codice rilevanti dal file * .c e * .cpp? Penso che possa migliorare l'esposizione della domanda. – OrenIshShalom

risposta

335

serie La C++ garantisce la seguente:

static_cast ing un puntatore da e per void* conserva l'indirizzo. Cioè, in seguito, a, b e c puntano tutti allo stesso indirizzo:

int* a = new int(); 
void* b = static_cast<void*>(a); 
int* c = static_cast<int*>(b); 

reinterpret_cast garantisce solo che se si esegue il cast di un puntatore a un tipo diverso, e poi reinterpret_cast indietro al tipo originale, ottieni il valore originale. Quindi, in quanto segue:

int* a = new int(); 
void* b = reinterpret_cast<void*>(a); 
int* c = reinterpret_cast<int*>(b); 

A e C contengono lo stesso valore, ma il valore di b non è specificato. (in pratica conterrà tipicamente lo stesso indirizzo di a e c, ma non è specificato nello standard, e potrebbe non essere vero su macchine con sistemi di memoria più complessi.)

Per la trasmissione da e verso il void *, è preferibile il static_cast.

+16

Mi piace il fatto che "b" non sia definito. Ti impedisce di fare cose stupide con esso. Se lanci qualcosa a un altro tipo di puntatore, stai chiedendo dei problemi e il fatto che non puoi dipendere da questo ti rende più attento. Se avessi usato static_cast <> sopra quale usare è la 'b' comunque? –

+2

Ho pensato che reinterpret_cast <> ha garantito lo stesso schema di bit. (che non è lo stesso di un puntatore valido per un altro tipo). –

+1

@Martin - reinterpret_cast <> non garantisce lo stesso pattern di bit. "La mappatura eseguita da reinterpret_cast <> è definita dall'implementazione." (C++ 03 5.3.10). Tuttavia, lo standard osserva che "è destinato a non sorprendersi". –

-14

Leggi il FAQ! Tenere i dati C++ in C può essere rischioso.

In C++, un puntatore a un oggetto può essere convertito in void * senza alcun calchi. Ma non è vero il contrario. Avresti bisogno di un static_cast per riavere il puntatore originale.

16

Il significato di reinterpret_cast non è definito dallo standard C++. Quindi, in teoria un reinterpret_cast potrebbe mandare in crash il tuo programma. In pratica i compilatori cercano di fare ciò che ti aspetti, cioè interpretare i pezzi di ciò che stai passando come se fossero il tipo a cui ti lanci. Se sai cosa fanno i compilatori che utilizzerai con reinterpret_cast puoi usarlo, ma dire che è portatile mentire.

Per il caso che descrivi, e quasi tutti i casi in cui potresti prendere in considerazione lo reinterpret_cast, puoi utilizzare lo static_cast o qualche altra alternativa. Tra le altre cose lo standard ha questo da dire su quello che ci si può aspettare di static_cast (§5.2.9):

An rvalue of type “pointer to cv void” can be explicitly converted to a pointer to object type. A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value.

Quindi per la vostra caso d'uso, sembra abbastanza chiaro che il comitato di standardizzazione destinato per l'uso static_cast .

+5

Non si blocca del tutto il programma. Lo standard offre alcune garanzie su reinterpret_cast. Solo non tante quante le persone spesso si aspettano. – jalf

+0

Beh, il reinterpret_cast stesso probabilmente non si arresterebbe in modo anomalo, ma potrebbe restituire alcuni risultati fasulli che, quando si tenta di utilizzarlo, potrebbero causare un arresto anomalo. – flodin

+1

Non se lo si utilizza correttamente. Cioè, reinterpret_cast da A a B a A è perfettamente sicuro e ben definito. Ma il valore di B non è specificato, e sì, se ci si basa su questo, potrebbero accadere cose brutte. Ma il cast stesso è abbastanza sicuro, purché tu lo abbia usato solo nel modo in cui lo standard lo consente. ;) – jalf

115

Un caso in cui è necessario reinterpret_cast è quando si interfaccia con tipi di dati opachi. Ciò si verifica frequentemente nelle API del fornitore su cui il programmatore non ha alcun controllo. Ecco un esempio inventato in cui un venditore fornisce un'API per archiviare e recuperare dati globali arbitraria:

// vendor.hpp 
typedef struct _Opaque * VendorGlobalUserData; 
void VendorSetUserData(VendorGlobalUserData p); 
VendorGlobalUserData VendorGetUserData(); 

Per utilizzare questa API, il programmatore deve lanciare i loro dati per VendorGlobalUserData e viceversa. static_cast non funziona, si deve usare reinterpret_cast:

// main.cpp 
#include "vendor.hpp" 
#include <iostream> 
using namespace std; 

struct MyUserData { 
    MyUserData() : m(42) {} 
    int m; 
}; 

int main() { 
    MyUserData u; 

     // store global data 
    VendorGlobalUserData d1; 
// d1 = &u;           // compile error 
// d1 = static_cast<VendorGlobalUserData>(&u);  // compile error 
    d1 = reinterpret_cast<VendorGlobalUserData>(&u); // ok 
    VendorSetUserData(d1); 

     // do other stuff... 

     // retrieve global data 
    VendorGlobalUserData d2 = VendorGetUserData(); 
    MyUserData * p = 0; 
// p = d2;           // compile error 
// p = static_cast<MyUserData *>(d2);    // compile error 
    p = reinterpret_cast<MyUserData *>(d2);   // ok 

    if (p) { cout << p->m << endl; } 
    return 0; 
} 

Di seguito è un'implementazione artificiosa delle API del campione:

// vendor.cpp 
static VendorGlobalUserData g = 0; 
void VendorSetUserData(VendorGlobalUserData p) { g = p; } 
VendorGlobalUserData VendorGetUserData() { return g; } 
+5

Sì, questo è l'unico uso significativo di reinterpret_cast a cui riesco a pensare. – jalf

+7

Questa potrebbe essere una domanda tardiva, ma perché l'API del fornitore non usa "void *" per quello? – Xeo

+76

@Xeo perché l'API del fornitore fa schifo. –

0
template <class outType, class inType> 
outType safe_cast(inType pointer) 
{ 
    void* temp = static_cast<void*>(pointer); 
    return static_cast<outType>(temp); 
} 

ho cercato di concludere e ha scritto un semplice getto di sicurezza utilizzando i modelli . Si noti che questa soluzione non garantisce il cast dei puntatori su una funzione.

+0

Cosa? Perché preoccuparsi? Questo è esattamente ciò che 'reinterpret_cast' già fa in questa situazione:" Un puntatore a oggetti può essere convertito esplicitamente in un puntatore a oggetti di un tipo diverso. [72] Quando un _provalore_ 'v' di tipo puntatore oggetto viene convertito nel puntatore dell'oggetto digita "puntatore a _cv_' T' ", il risultato è' static_cast (static_cast (v)) '." - N3797. –

+0

Per quanto riguarda lo standard 'C++ 2003' posso ** NOT ** trovare che' reinterpret_cast' fa 'static_cast (static_cast (v))' –

+0

OK, è vero, ma non mi interessa una versione da 13 anni fa, e non dovrebbe essere la maggior parte dei programmatori se (come è probabile) possono evitarlo. Le risposte e i commenti dovrebbero riflettere l'ultimo standard disponibile, se non diversamente specificato ... IMHO. Ad ogni modo, credo che il Comitato abbia sentito la necessità di aggiungere esplicitamente questo dopo il 2003. (perché IIRC, era lo stesso in C++ 11) –

9

Un uso di reinterpret_cast è se si desidera applicare operazioni bit per bit a (IEEE 754) galleggia. Un esempio di questo è stato l'inversa Fast radice quadrata trucco:

https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code

Tratta la rappresentazione binaria del flottante come un intero, si sposta a destra e sottrae da una costante, dimezzando in tal modo e negando l'esponente . Dopo riconversione un galleggiante, è sottoposto ad un'iterazione Newton-Raphson per rendere questa approssimazione più precisa:

float Q_rsqrt(float number) 
{ 
    long i; 
    float x2, y; 
    const float threehalfs = 1.5F; 

    x2 = number * 0.5F; 
    y = number; 
    i = * (long *) &y;      // evil floating point bit level hacking 
    i = 0x5f3759df - (i >> 1);    // what the deuce? 
    y = * (float *) &i; 
    y = y * (threehalfs - (x2 * y * y)); // 1st iteration 
// y = y * (threehalfs - (x2 * y * y)); // 2nd iteration, this can be removed 

    return y; 
} 

Questo è stato scritto in C, quindi utilizza C getta, ma l'analogo C++ cast è il reinterpret_cast.

+1

'errore: cast non valida di un'espressione rvalue di tipo 'int64_t {alias long long int}' nel tipo 'double &' reinterpret_cast ((reinterpret_cast (d) >> 1) + (1L << 61)) '- http://ideone.com/6S4ijc – Orwellophile

+0

Scusa, stavo cercando di riprodurre il codice dalla memoria (molto confusa!). Ora è stato sostituito con il codice originale corretto. –

+0

Lo standard dice che questo comportamento non è definito: http://en.cppreference.com/w/cpp/language/reinterpret_cast (sotto "digita aliasing") –

-3

Risposta rapida: utilizzare static_cast se compila, altrimenti ricorrere a reinterpret_cast.

0

In primo luogo Se avete alcuni dati in un tipo specifico come int qui:

int x = 0x7fffffff://==nan in binary representation 

poi si desidera accedere alla stessa variabile come altro tipo come float: Si può decidere tra

float y = reinterpret_cast<float&>(x); 

//this could only be used in cpp, looks like a function with template-parameters 

o

float y = *(float*)&(x); 

//this could be used in c and cpp 

BREVE: significa che la stessa memoria viene utilizzata come un tipo diverso. In questo modo è possibile convertire le rappresentazioni binarie di float come tipo int come sopra in float. 0x80000000 è -0 per esempio (la mantissa e l'esponente sono nulli ma il segno, il msb, è uno. Questo funziona anche per i doppi e i doppi lunghi.

OTTIMIZZARE: Penso che reinterpret_cast sarebbe ottimizzato in molti compilatori, mentre il c-casting è fatto da pointerarithmetic (il valore deve essere copiato nella memoria, perché i puntatori non possono puntare a cpu- registers).

NOTA: in entrambi i casi è necessario salvare il valore del cast in una variabile prima del lancio! Questa macro potrebbe aiutare:

#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; }) 
26

La risposta breve: Se non sai cosa sta per reinterpret_cast, non ne fanno uso. Se ne avrai bisogno in futuro, lo saprai.

risposta completa:

Consideriamo tipi di numero di base.

Quando si converte ad esempio int(12) in unsigned float (12.0f), il processore deve richiamare alcuni calcoli poiché entrambi i numeri hanno una rappresentazione bit diversa. Questo è il significato di static_cast.

D'altra parte, quando si chiama reinterpret_cast la CPU non richiama alcun calcolo. Tratta semplicemente un insieme di bit nella memoria come se avesse un altro tipo. Pertanto, quando si converte int* in float* con questa parola chiave, il nuovo valore (dopo il puntatore dereferecing) non ha nulla a che fare con il vecchio valore in significato matematico.

Esempio: E 'vero che reinterpret_cast non è portabile per una ragione - ordine dei byte (endian). Ma questo è spesso sorprendentemente il miglior motivo per usarlo. Immaginiamo l'esempio: devi leggere il numero binario a 32 bit dal file e sai che è big endian. Il codice deve essere generico e funziona correttamente sui sistemi big endian (ad esempio ARM) e little endian (ad esempio x86). Quindi devi controllare l'ordine dei byte. E 'ben noto in fase di compilazione in modo da poter scrivere constexpr funzione:

constexpr bool is_little_endian() { 
    unsigned short x=0x0001; 
    auto p = reinterpret_cast<unsigned char*>(&x); 
    return *p != 0; 
} 

Spiegazione: la rappresentazione binaria di x in memoria potrebbe essere 0000'0000'0000'0001 (grande) o 0000'0001'0000'0000 (little endian). Dopo la reinterpretazione, il byte sotto il puntatore p potrebbe essere rispettivamente 0000'0000 o 0000'0001. Se si utilizza la fusione statica, sarà sempre 0000'0001, indipendentemente dall'uso di endianness.