2013-02-21 22 views
8
const unsigned char* p; 
int64_t u = ...; // ?? 

Qual è il modo consigliato di leggere un intero binario little endian a 64 bit dagli 8 byte puntati da p? Su x64 dovrebbe essere eseguita un'istruzione a macchina singola, ma sono necessari swap hardware di big-endian. Come si fa a farlo in modo ottimale e portabile?Leggere gli interi binari

La soluzione di Carl è buona, abbastanza portatile ma non ottimale. Questo solleva la domanda: perché C/C++ non fornisce un modo migliore e standardizzato per farlo? Non è un costrutto insolito.

+0

'int64_t' non è portatile. Ho la sensazione che anche la soluzione più portabile dovrà fare alcune ipotesi (come big e little-endian sono le uniche possibilità). –

+1

'int64_t' è standard C++ 11. Presume che il tipo sia disponibile però. – XTF

+1

@XTF Esattamente, questo è ciò che intendo. Se il tipo è disponibile come opzione, non è portatile. È solo portatile tra quei sistemi che hanno un tipo a 64 bit. –

risposta

7

Il comunemente visto:

u = (int64_t)(((uint64_t)p[0] << 0) 
    + ((uint64_t)p[1] << 8) 
    + ((uint64_t)p[2] << 16) 
    + ((uint64_t)p[3] << 24) 
    + ((uint64_t)p[4] << 32) 
    + ((uint64_t)p[5] << 40) 
    + ((uint64_t)p[6] << 48) 
    + ((uint64_t)p[7] << 56)); 

è praticamente l'unico gioco in città per la portabilità - è altrimenti difficile da evitare potenziali problemi di allineamento.

Questa risposta presuppone un char 8 bit. Se potresti aver bisogno di supportare dimensioni diverse char s, avrai bisogno di una definizione del preprocessore che controlli CHAR_BIT e faccia la cosa giusta per ciascuno.

+0

Con il mio ciclo, una moltiplicazione per 'CHAR_BIT' fa il trucco, credo :) –

+0

@ H2CO3, sì, funzionerà, ma il compilatore non ha l'obbligo di ottimizzare/srotolare quel ciclo. Probabilmente funzionerà anche con la maggior parte dei compilatori. –

+0

@CarlNorum Eh, spaccare i capelli ... Non sono convinto che il compilatore ** lo farà srotolare, naturalmente. –

4

Carl Norum ha ragione: se vuoi essere leggibile, puoi scrivere un ciclo (il compilatore lo srotolerà comunque). Ciò si occuperà anche di non-8-bit char s.

u = 0; 
const int n = 64/CHAR_BIT + !!(64 % CHAR_BIT); 
for (int i = 0; i < n; i++) { 
    u += (uint64_t)p[i] << (i * CHAR_BIT); 
} 
+0

Non dovrebbe farlo solo 4 volte se CHAR_BIT = 16? – XTF

+0

@XTF Questo looperà solo 4 volte se 'CHAR_BIT == 16', poiché' 64/16 + !! (64% 16) = 4 + !! 0 = 4 + 0 = 4'. –

+0

Se 'CHAR_BIT' è' 9' (bit di parità), le tue letture saranno troppo corte o lette troppo lontano. –

0

Ho utilizzato il seguente codice per invertire l'ordine dei byte per qualsiasi variabile. L'ho usato per convertire tra diverse "Endianess".

// Reverses the order of bytes in the specified data 
void ReverseBytes(LPBYTE pData, int nSize) 
{ 
    int i, j; 

    for (i = 0, j = nSize - 1; i < j; i++, j--) 
    { 
     BYTE nTemp = pData[i]; 
     pData[i] = pData[j]; 
     pData[j] = nTemp; 
    } 
} 
+2

Non va bene, non devi modificare i tuoi input. – XTF

+4

No, non è male. Perché suggeriresti una regola così arbitraria? Lo sto facendo da parecchio tempo. Dipende interamente dalle tue esigenze. –

+0

La regola non è arbitraria. 'ReverseBytes' non è di per sé il problema; il chiamante di una tale routine probabilmente si aspetta che i dati puntati siano invertiti. Il problema è che se si utilizza 'ReverseBytes' in una routine per convertire i dati little-endian in un numero intero nativo, il chiamante di quella routine probabilmente non si aspetta che i dati di input vengano modificati. Quindi stai facendo qualcosa di inaspettato che può causare un bug. –

Problemi correlati