BinaryReader.ReadInt64
è little endian di progettazione. Dalla documentazione:
BinaryReader legge questo tipo di dati in formato little-endian.
Infatti, è possibile ispezionare la fonte per BinaryReader.ReadInt64
utilizzando Reflector.
public virtual long ReadInt64() {
this.FillBuffer(8);
uint num = (uint) (((this.m_buffer[0] |
(this.m_buffer[1] << 0x08)) |
(this.m_buffer[2] << 0x10)) |
(this.m_buffer[3] << 0x18));
uint num2 = (uint) (((this.m_buffer[4] |
(this.m_buffer[5] << 0x08)) |
(this.m_buffer[6] << 0x10)) |
(this.m_buffer[7] << 0x18));
return (long) ((num2 << 0x20) | num);
}
Questa che BinaryReader.ReadInt64
recita little endian indipendente dell'architettura sottostante.
Ora, BitConverter.ToInt64
si supponga di rispettare l'endianness della macchina sottostante. In Riflettore possiamo vedere
public static unsafe long ToInt64(byte[] value, int startIndex) {
// argument checking elided
fixed (byte* numRef = &(value[startIndex])) {
if ((startIndex % 8) == 0) {
return *(((long*) numRef));
}
if (IsLittleEndian) {
int num = (numRef[0] << 0x00) |
(numRef[1] << 0x08) |
(numRef[2] << 0x10) |
(numRef[3] << 0x18);
int num2 = (numRef[4] << 0x00) |
(numRef[5] << 0x08) |
(numRef[6] << 0x10) |
(numRef[7] << 0x18);
return (((long) ((ulong) num)) | (num2 << 0x20));
}
int num3 = (numRef[0] << 0x18) |
(numRef[1] << 0x10) |
(numRef[2] << 0x08) |
(numRef[3] << 0x00);
int num4 = (numRef[4] << 0x18) |
(numRef[5] << 0x10) |
(numRef[6] << 0x08) |
(numRef[7] << 0x00);
return (((long) ((ulong) num4)) | (num3 << 0x20));
}
Così quello che vediamo qui è che se startIndex
è congruente a zero modulo otto che un cast diretta è fatto da otto byte a partire dall'indirizzo numRef
. Questo caso è gestito specialmente a causa di problemi di allineamento.La riga di codice
return *(((long *) numRef));
traduce direttamente al
ldloc.0 ;pushes local 0 on stack, this is numRef
conv.i ;pop top of stack, convert to native int, push onto stack
ldind.i8 ;pop address off stack, indirect load from address as long
ret ;return to caller, return value is top of stack
Così vediamo che in questo caso la chiave è l'istruzione ldind.i8
. La CLI è agnostica sulla endianità della macchina sottostante. Permette al compilatore JIT di gestire questo problema. Su una macchina little-endian, ldind.i8
caricherà indirizzi più alti in bit più significativi e su una macchina big-endian ldind.i8
caricherà indirizzi più alti in byte meno significativi. Pertanto, in questo caso, endianness viene gestito correttamente.
Nell'altro caso, è possibile vedere che esiste un controllo esplicito della proprietà statica BitConverter.IsLittleEndian
. Nel caso di little endian il buffer viene interpretato come little endian (in modo che la memoria { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }
venga interpretata come il lungo 0x0706050403020100
) e nel caso di big endian il buffer venga interpretato come big endian (in modo che la memoria venga interpretata come long 0x0001020304050607
) . Quindi, per BitConverter
tutto si riduce alla funzionalità del underyling. Prendo atto che sei su un chip Intel su Windows 7 x64. I chip Intel sono poco endian. Faccio notare che in Reflector, il costruttore statico per BitConverter
è definito come il seguente:
static BitConverter() {
IsLittleEndian = true;
}
Questo è sulla mia macchina Windows Vista x64. (Potrebbe essere diverso, ad esempio, su .NET CF su XBox 360.) Windows 7 x64 non ha alcun motivo per essere diverso. Di conseguenza, sei sicuro che BitConverter.IsLittleEndian
è false
? Dovrebbe essere true
e quindi il comportamento che stai vedendo è corretto.
tag Aggiunto per di cercare :) – spender
Può darci le specifiche della macchina (processore, MB, O/S)? – GrayWizardx
Questo è un Intel Core 2, Windows 7. Dovrei aggiungere anche che BitConverter.IsLittleEndian restituisce false. – Amy