2009-12-16 10 views
11

tengo los siguientes tipos de valores: .NETDisposición de .NET tipo de valor en la memoria

[StructLayout(LayoutKind.Sequential)] 
public struct Date 
{ 
    public UInt16 V; 
} 

[StructLayout(LayoutKind.Sequential)] 
public struct StringPair 
{ 
    public String A; 
    public String B; 
    public String C; 
    public Date D; 
    public double V; 
} 

tengo código que se pasa un puntero a un tipo de valor de código no administrado, junto con las compensaciones descubiertos llamando al Sistema .Runtime.InteropServices.Marshal.OffsetOf. El código no administrado está rellenando la fecha y los valores dobles.

Las compensaciones que se reportan para la estructura StringPair son exactamente lo que se espera: 0, 8, 16, 24, 32

tengo el siguiente código en una función de prueba:

FieldInfo[] fields = typeof(StringPair).GetFields(BindingFlags.Instance|BindingFlags.Public); 

for (int i = 0; i < fields.Length; i++) 
{ 
    int offset = System.Runtime.InteropServices.Marshal.OffsetOf(typeof(StringPair), fields[i].Name).ToInt32(); 

    Console.WriteLine(String.Format(" >> field {0} @ offset {1}", fields[i].Name, offset)); 
} 

Que imprime exactamente estas compensaciones.

>> field A @ offset 0 
>> field B @ offset 8 
>> field C @ offset 16 
>> field D @ offset 24 
>> field V @ offset 32 

entonces tengo algún código de prueba: foreach (StringPair par en pares) { Fecha d = pair.D; double v = pair.V; ...

que tiene la siguiente ensamblador asociado con él en el depurador:

   Date d = pair.D; 
0000035d lea   rax,[rbp+20h] 
00000361 add   rax,20h 
00000367 mov   ax,word ptr [rax] 
0000036a mov   word ptr [rbp+000000A8h],ax 
00000371 movzx  eax,word ptr [rbp+000000A8h] 
00000378 mov   word ptr [rbp+48h],ax 

       double v = pair.V; 
0000037c movsd  xmm0,mmword ptr [rbp+38h] 
00000381 movsd  mmword ptr [rbp+50h],xmm0 

Se está cargando el campo D en el offset 32 ​​(0x20) y el campo V en el offset 24 (0x38-0x20) El JIT ha cambiado el orden. El depurador de Visual Studio también muestra este orden invertido.

¿Por qué? Me he estado tirando de los pelos tratando de ver dónde va mi lógica. Si cambio el orden de D y V en la estructura, todo funciona, pero este código debe poder tratar con una arquitectura de complemento donde otros desarrolladores hayan definido la estructura, y no se puede esperar que recuerden las reglas de diseño arcano.

Respuesta

11

La información que se obtiene de la clase Marshal sólo es relevante si el tipo de realidad se calculan las referencias. El diseño de la memoria interna de una estructura administrada no se puede descubrir a través de ningún medio documentado, aparte de leer el código del ensamblado.

Lo que significa que CLR es libre de reorganizar el diseño de la estructura y optimizar el embalaje. Al intercambiar los campos D y V, su estructura es más pequeña debido a los requisitos de alineación de un doble. Ahorra 6 bytes en su máquina de 64 bits.

No estoy seguro de por qué esto sería un problema para usted, no debería ser así. Considere Marshal.StructureToPtr() para obtener la estructura dispuesta de la manera que desee.

+1

Gracias: me había perdido el hecho de que los métodos Marshall. * Solo se aplicaban al puntero estructurado. Por motivos de rendimiento, esperaba evitar una copia adicional de los datos, pero eso parece bastante inevitable si quiero admitir estructuras –

+1

+1 arbitrarias para aclarar que el diseño de la estructura JIT es un problema completamente diferente de lo que CLR especifica, ya que-- en principio, no puede ser visto por, y por lo tanto no puede importar, los programas administrados. Me di cuenta de que JITer parece colocar los campos administrados antes sin administrar. –

+0

El diseño de bits exacto en la memoria de (una instancia de) un tipo de valor puede tener cierta relevancia para decidir si se implementa IEtabletable (y las anulaciones recomendadas de object.Equals y GetHashCode). Si no implementa IEquatable, entonces creo que el código generado por defecto realiza una comparación bit a bit de los bits en la memoria. Si hay GAPS en el diseño, puede perder tiempo comparando bits que no importan (en C++, también podría arriesgarse a la corrección ya que esos bits podrían no inicializarse, pero creo que siempre están en C#). Conocer el diseño informa la decisión sobre qué implementar. –

13

Si necesita la disposición explícita ... uso disposición explícita ...

[StructLayout(LayoutKind.Explicit)] 
public struct StringPair 
{ 
    [FieldOffset(0)] public String A; 
    [FieldOffset(8)] public String B; 
    [FieldOffset(16)] public String C; 
    [FieldOffset(24)] public Date D; 
    [FieldOffset(32)] public double V; 
} 
+2

podría haber jurado que había probado que en algún momento de los últimos días .. pero parece que no funciona ahora. Sin embargo, el punto es que el * secuencial * debería * funcionar y el marco está informando compensaciones que no coinciden con las que realmente está usando. Realmente no quiero que los usuarios de esta arquitectura de complementos tengan que especificar un atributo y hacer los cálculos ellos mismos para que esto funcione. –

1

dos cosas:

  • StructLayout(Sequential) no garantiza el embalaje. Es posible que desee utilizar Pack=1, de lo contrario, las plataformas de 32 y 64 bits podrían ser diferentes.

  • y la cadena es una referencia, no un puntero.Si la longitud de la cadena es siempre fija, es posible que desee utilizar matrices de char fijos:

    public struct MyArray // This code must appear in an unsafe block 
    { 
        public fixed char pathName[128]; 
    } 
    
Cuestiones relacionadas