2013-01-11 13 views
13

void* è una funzionalità utile di C e linguaggi derivati. Ad esempio, è possibile utilizzare void* per memorizzare puntatori di oggetti C obiettivo in una classe C++.Perché posso trasmettere int e BOOL per annullare *, ma non float?

stavo lavorando su un quadro conversione del tipo di recente e per mancanza di tempo era un po 'pigro - così ho usato void* ... È così che a questa domanda si avvicinò:

Perché posso typecast int a void *, ma non galleggiare a vuoto *?

+0

I riferimenti Objective-C sono di tipo 'id'. Vedi http://stackoverflow.com/questions/1304176/objective-c-difference-between-id-and-void – Potatoswatter

risposta

21

BOOL non è un tipo C++. Probabilmente è typedef o definito da qualche parte e, in questi casi, sarebbe lo stesso di int. Windows, per esempio, ha questo in Windef.h:

typedef int     BOOL; 

così la tua domanda si riduce a, perché si può typecast int * di annullare, ma non galleggiare a void *?

int a void * è ok ma in genere non è consigliato (e alcuni compilatori ne metteranno in guardia) perché sono intrinsecamente uguali nella rappresentazione. Un puntatore è fondamentalmente un numero intero che punta a un indirizzo in memoria.

float to void * non è ok perché l'interpretazione del valore float e dei bit effettivi che la rappresentano sono differenti. Ad esempio, se si fa:

float x = 1.0; 

ciò che fa è imposta la memoria a 32 bit a 00 00 80 3f (la rappresentazione effettiva del valore float 1,0 in IEEE singola precisione). Quando lanci un galleggiante su un vuoto *, l'interpretazione è ambigua. Intendi il puntatore che punta alla posizione 1 in memoria? o intendi il puntatore che punta alla posizione 3f800000 (assumendo little endian) in memoria?

Ovviamente, se sei sicuro di quale dei due casi desideri, c'è sempre un modo per aggirare il problema. Ad esempio:

void* u = (void*)((int)x);  // first case 
    void* u = (void*)(((unsigned short*)(&x))[0] | (((unsigned int)((unsigned short*)(&x))[1]) << 16)); // second case 
+2

'BOOL' è un typedef'd' signed char' in ObjC. –

+2

+1 per * "[...] Quando lanci un float su un vuoto \ *, l'interpretazione è ambigua. Intendi il puntatore che punta alla posizione 1 in memoria o intendi il puntatore che punta alla posizione? 3f800000 (assumendo little endian) in memoria? "* – Nawaz

+1

Nitpick:' 0000803f' è un ordine byte little-endian; '3f800000' è ciò che sembra come un' int' indipendentemente da endianness. E 'int' to' void * 'non è strettamente OK; guarda la mia risposta – Potatoswatter

17

I puntatori vengono solitamente rappresentati internamente dalla macchina come numeri interi. C consente di eseguire il cast avanti e indietro tra il tipo di puntatore e il tipo intero. (Un valore puntatore può essere convertito in un numero intero abbastanza grande da trattenerlo, e viceversa)

Utilizzare void* per contenere valori interi non convenzionali. Non è garantito dal linguaggio per funzionare, ma se vuoi essere sciatto e costringerti a dedicarti a Intel e ad altre piattaforme comuni, sarà fondamentalmente da analizzare.

In effetti ciò che si sta facendo è utilizzare void* come un contenitore generico di molti byte tuttavia vengono utilizzati dalla macchina per i puntatori. Ciò differisce tra le macchine a 32 e 64 bit. Quindi convertire long long in void* perderebbe bit su una piattaforma a 32 bit.

Per quanto riguarda i numeri in virgola mobile, l'intenzione di (void*) 10.5f è ambigua. Vuoi arrotondare 10.5 a un numero intero, quindi convertirlo in un puntatore senza senso? No, vuoi che il pattern di bit utilizzato dalla FPU sia inserito in un puntatore senza senso. Questo può essere ottenuto assegnando float f = 10.5f; void *vp = * (uint32_t*) &f;, ma sii avvisato che questo non ha senso: i puntatori non sono memoria generica per bit.

La migliore memoria generica per bit è gli array char, a proposito. Gli standard linguistici garantiscono che la memoria possa essere manipolata tramite char*. Ma devi badare ai requisiti di allineamento dei dati.

+0

Ri: 2 ° paragrafo: puoi farlo più facilmente con 'uintptr_t'. – asveikau

+0

@asveikau 'uintptr_t' è abbastanza grande da contenere un valore del puntatore. Sta cercando di usare "void *" per mantenere un valore intero, che è il contrario. Secondo le specifiche della lingua, è consentito arresti anomali. C++ 11 aggiunge anche disposizioni per la macchina che verifica che i valori del puntatore siano stati calcolati correttamente, come mezzo di maggiore affidabilità. – Potatoswatter

+0

Ah, OK. Hai ragione. Avrebbe dovuto prestare più attenzione prima di commentare. – asveikau

1

Standard dice che 752 Un numero intero può essere convertito in qualsiasi tipo di puntatore. Non dice nulla sulla conversione pointer-float.

Problemi correlati