2009-02-13 10 views
12

Desidero leggere i byte sizeof(int) da un array char*.Lettura di byte di dimensioni "integer" da un array char *.

a) In quale scenario è necessario preoccuparsi se è necessario verificare l'endianità?

b) Come leggeresti i primi 4 byte prendendo in considerazione o meno endianness.

MODIFICA: I byte che ho letto devono essere confrontati con un valore intero.

Qual è l'approccio migliore per andare su questo problema

+0

Sono un po 'confuso su quello che stai cercando di fare. Potresti scrivere qualche pseudocodice, ad esempio? Stai cercando di analizzare gli interi dall'array di caratteri? –

+0

Sto cercando di trovare i byte sizeof (int) da un array char * e provare a confrontarlo con un numero intero. La fonte dei dati è una macchina diversa. – kal

risposta

1

Non dovrebbe essere necessario preoccuparsi di endianess a meno che non si sta leggendo il byte da una fonte creato su una macchina diversa, per esempio un flusso di rete.

Dato che, non puoi semplicemente usare un ciclo for?

void ReadBytes(char * stream) { 
    for (int i = 0; i < sizeof(int); i++) { 
     char foo = stream[i]; 
     } 
    } 
} 

Stai chiedendo qualcosa di più complicato di così?

+0

I miei dati vengono effettivamente creati da un'altra fonte – kal

1

È necessario preoccuparsi di endianess solo se i dati che si sta leggendo sono composti da numeri maggiori di un byte.
se stai leggendo i byte sizeof (int) e prevedi di interpretarli come un interno, l'endianità fa la differenza. essenzialmente endianness è il modo in cui una macchina interpreta una serie di più di 1 byte in un valore numerico.

3

dipende da come si desidera leggere loro, ho la sensazione che si desidera lanciare 4 byte in un intero, così facendo sulla rete in streaming i dati di solito finiscono in qualcosa di simile:

int foo = *(int*)(stream+offset_in_stream); 
+1

Ciò potrebbe causare un accesso non allineato. – gimpf

+0

@gimpf: Sono curioso: su quali sistemi questo porterà effettivamente ad un errore? – Christoph

+0

I.e. su 80486 e qualsiasi CPU migliore con il set Align-Flag. –

18

Do vuoi dire una cosa del genere ?:

char* a; 
int i; 
memcpy(&i, a, sizeof(i)); 

Devi solo preoccuparti di endianess se la fonte dei dati è da una piattaforma diversa, come un dispositivo.

+0

Cosa potrebbe essere più ovvio? : D –

+0

Questo è un buon modo legale senza rompere le regole di tipo puning/aliasing. Per quelli che si chiedono, "ma sembra più lento di un cast!" a) il cast è un comportamento indefinito, quindi non andare lì b) il codice generato non è diverso per x86/x64: https://godbolt.org/g/gxtVFZ – Eloff

1

Basta usare un ciclo for che si sposta sull'array nei blocchi sizeof (int).
Utilizzare la funzione ntohl (trovata nell'intestazione <arpa/inet.h>, almeno su Linux) per convertire da byte nell'ordine di rete (l'ordine di rete è definito big-endian) nell'ordine di byte locale. Questa funzione di libreria è implementata per eseguire la corretta conversione da rete a host per qualsiasi processore su cui stai lavorando.

+0

Naturalmente, questo si applica solo se sei in realtà leggendo qualcosa dalla rete ... – gimpf

+0

Ok, ha dichiarato nel _comment_ che sta leggendo da una macchina diversa. Beh, forse fatto bruciando/leggendo un CD, ma più probabilmente in realtà intendeva un qualche tipo di rete. – gimpf

9

a) È necessario preoccuparsi solo di "endianità" (cioè scambio di byte) se i dati sono stati creati su una macchina big-endian e vengono elaborati su una macchina little-endian o viceversa. Ci sono molti modi in cui ciò può accadere, ma qui ci sono un paio di esempi.

  1. Si ricevono dati su una macchina Windows tramite un socket. Windows utilizza un'architettura little-endian mentre i dati di rete sono "supposti" per essere in formato big-endian.
  2. Si elabora un file di dati che è stato creato su un sistema con un diverso "endianness".

In entrambi i casi, è necessario scambiare byte tutti i numeri superiori a 1 byte, ad es., corti, inte, long, double, ecc. Tuttavia, se si hanno sempre a che fare con dati della stessa piattaforma, i problemi di endian non sono di alcuna importanza.

b) In base alla tua domanda, sembra che tu abbia un puntatore char e desideri estrarre i primi 4 byte come un int e quindi affrontare eventuali problemi di endian. Per eseguire l'estrazione, utilizzare:

int n = *(reinterpret_cast<int *>(myArray)); // where myArray is your data 

Ovviamente, questo presuppone che myArray non sia un puntatore nullo; in caso contrario, questo si bloccherà in quanto dereferenzia il puntatore, quindi impiegare un buon schema di programmazione difensiva.

Per scambiare i byte su Windows, è possibile utilizzare le funzioni ntohs()/ntohl() e/o htons()/htonl() definite in winsock2.h. Oppure si può scrivere alcune semplici routine per fare questo in C++, ad esempio:

inline unsigned short swap_16bit(unsigned short us) 
{ 
    return (unsigned short)(((us & 0xFF00) >> 8) | 
          ((us & 0x00FF) << 8)); 
} 

inline unsigned long swap_32bit(unsigned long ul) 
{ 
    return (unsigned long)(((ul & 0xFF000000) >> 24) | 
          ((ul & 0x00FF0000) >> 8) | 
          ((ul & 0x0000FF00) << 8) | 
          ((ul & 0x000000FF) << 24)); 
} 
+1

devi dire che il primo snippet di codice ha lo stesso problema di Daniels: può accedere a dati non allineati che non sono adatti per int * –

3

Il modo più semplice per risolvere questo è quello di assicurarsi tutto ciò genera il byte lo fa in un endianness coerente. In genere l ' "ordine di byte di rete" usato da vari roba TCP/IP è migliore: la biblioteca routine htonl e ntohl lavoro molto bene con questo, e sono di solito abbastanza ben ottimizzato.

Tuttavia, se non si utilizza l'ordine dei byte di rete, potrebbe essere necessario eseguire altre operazioni in . Hai bisogno di sapere due cose: la dimensione di un intero e l'ordine dei byte. Una volta che lo sai, sai quanti byte estrarre e in quale ordine mettere insieme in un int.

qualche esempio di codice che assume sizeof (int) è il giusto numero di byte:

#include <limits.h> 

int bytes_to_int_big_endian(const char *bytes) 
{ 
    int i; 
    int result; 

    result = 0; 
    for (i = 0; i < sizeof(int); ++i) 
     result = (result << CHAR_BIT) + bytes[i]; 
    return result; 
} 

int bytes_to_int_little_endian(const char *bytes) 
{ 
    int i; 
    int result; 

    result = 0; 
    for (i = 0; i < sizeof(int); ++i) 
     result += bytes[i] << (i * CHAR_BIT); 
    return result; 
} 


#ifdef TEST 

#include <stdio.h> 

int main(void) 
{ 
    const int correct = 0x01020304; 
    const char little[] = "\x04\x03\x02\x01"; 
    const char big[] = "\x01\x02\x03\x04"; 

    printf("correct: %0x\n", correct); 
    printf("from big-endian: %0x\n", bytes_to_int_big_endian(big)); 
    printf("from-little-endian: %0x\n", bytes_to_int_little_endian(little)); 
    return 0; 
} 

#endif 
+0

Ora sostituisci "int" con "unsigned" e la tua risposta è corretta;) –

+1

sostituirò il + e + = con | e | = rispettivamente. è confuso usare gli operatori matematici qui imho. –

1

Perché leggere quando si può semplicemente confrontare?

bool AreEqual(int i, char *data) 
{ 
    return memcmp(&i, data, sizeof(int)) == 0; 
} 

Se ci si preoccupa di endianness quando è necessario convertire tutti gli interi in qualche forma invariabile. htonl e ntohl sono buoni esempi.

+0

Questo restituirà sempre false. Penso che tu intenda memcmp(), non memcpy(). –

+0

Grazie, risolto. – okutane

3

Come su

int int_from_bytes(const char * bytes, _Bool reverse) 
{ 
    if(!reverse) 
     return *(int *)(void *)bytes; 

    char tmp[sizeof(int)]; 

    for(size_t i = sizeof(tmp); i--; ++bytes) 
     tmp[i] = *bytes; 

    return *(int *)(void *)tmp; 
} 

usereste in questo modo:

int i = int_from_bytes(bytes, SYSTEM_ENDIANNESS != ARRAY_ENDIANNESS); 

Se siete su un sistema in cui la fusione void *-int * può comportare conflitti di allineamento, si possibile utilizzare

int int_from_bytes(const char * bytes, _Bool reverse) 
{ 
    int tmp; 

    if(reverse) 
    { 
     for(size_t i = sizeof(tmp); i--; ++bytes) 
      ((char *)&tmp)[i] = *bytes; 
    } 
    else memcpy(&tmp, bytes, sizeof(tmp)); 

    return tmp; 
}