2010-03-11 12 views
9

Mój problem polega na wysłaniu struktury między programem w C do programu C#.Rozmiar struktur w .NET

że wykonana ona strukturę, w C#

public struct NetPoint { 
    public float lat; // 4 bytes 
    public float lon; // 4 bytes 
    public int alt; // 4 bytes 
    public long time; // 8 bytes 
} 

Całkowita wielkość strukturze wynosi 20 bajtów.

Kiedy zrobić sizeof() w C++ tej struktury,

System.Diagnostics.Debug.WriteLine(
    "SizeOf(NetPoint) = " + 
    System.Runtime.InteropServices.Marshal.SizeOf(new NetPoint())); 

konsola debugowania pokazuje:

sizeof (Netpoint) = 24

ale spodziewałem się mieć 20 bajtów. Dlaczego widzę różnicę?

+0

Zobacz tutaj: http://www.vsj.co.uk/articles/display.asp?id=501 –

Odpowiedz

7

Właściwie, technicznie, struktura musi być równa minimalna z 20 bajtów. Jeśli przydzielisz więcej podczas wysyłania, odbiorca po prostu nie użyje/nie skopiuje ich. Problem jest zawsze niedorzeczny.

To powiedziawszy, widzę problem. Hmm. Myślę, że problem jest ostatni długi .... który IMHO dostaje wyrównany do ośmiu bajtów, wstrzykując cztery puste bajty przedtem. Myślę, że istnieje kara za wydajność, gdy element ośmiobajtowy nie jest wyrównany do granicy ośmiu bajtów.

Dołącz atrybuty StructLayout, aby ręcznie określić przesunięcie każdego elementu. Wtedy powinieneś być w stanie dopasować wszystko do siebie.

referencyjny: How to control the physical layout of the data fields in the .NET Framework 2.0

[StructLayout(LayoutKind.Sequential, Pack=1)] 
public struct NetPoint { 
    public float lat; // 4 bytes 
    public float lon; // 4 bytes 
    public int alt; // 4 bytes 
    public long time; // 8 bytes 
} 

że przynajmniej powinien wyrównać elementy do granicy jednego bajta. Możesz przejść dalej, definiując dokładny początek każdego elementu, jeśli to konieczne.

+0

Twoje rozwiązanie również działa. Dziękuję Ci bardzo. Twoje rozwiązanie wydaje się łatwiejsze niż odpowiedź Johna. Będę używał twojego rozwiązania .. Pa, – user291830

2

Spróbuj dodać atrybut [StructLayout (LayoutKind.Sequential, Pack = 1)] i zobacz, co się stanie. Podejrzewam 8-bajtowe dopełnienie, więc jest to 3 x 8 bajtów.

+1

nie, obiekt w .NET (z wyjątkiem prymitywów takich jak int) ma wielkość ponad sumę pola. – Andrey

9

Zgodnie z ogólną zasadą, procesory lubią mieć zmienne wyrównane w pamięci w miejscu, które jest nawet wielokrotnością ich rozmiaru, więc czterobajtowa liczba całkowita powinna znajdować się na adresie pamięci podzielnym na cztery, a na osiem -byte long powinno być pod adresem podzielnym przez osiem.

Projektanci języka C# (i C++) wiedzą o tym i będą wkładać wyściółkę w struktury, aby zapewnić niezbędne wyrównanie. Więc rzeczywisty układ swojej struktury wygląda następująco:

public struct NetPoint { 
    public float lat;   // 4 bytes Offset 0 
    public float lon;   // 4 bytes Offset 4 
    public int alt;   // 4 bytes Offset 8 
    int to_preserve_alignment; // 4 bytes Offset 12 
    public long time;   // 8 bytes Offset 16 
} 

Można rozwiązać ten problem poprzez długą pierwszą wartość, co do zasady, jeśli zawsze umieścić największe wartości na początku swoich struktur, wygrałeś włożono wkładkę, aby zachować wyrównanie elementów.

Można również naprawić dodając

[StructLayout(LayoutKind.Sequential, Pack = 4)] 

przed deklaracji struktury, ale to spowoduje mis wyrównany long time który boli wydajność.W przypadku niektórych procesorów bardzo często boli wydajność. (Na przykład ALPHA AXP spowodował błąd w przypadku źle wyrównanych elementów). x86 Procesory mają tylko nieznaczną obniżkę wydajności, ale istnieje niebezpieczeństwo, że przyszłe procesory będą miały znaczną karę za wydajność, więc najlepiej jest zaprojektować swoje struktury, aby odpowiednio dopasować (a nie je pakować), jeśli możesz.

+0

Masz pełne prawo! Bardzo dziękuję – user291830

+1

Wow, uwielbiam tego typu opinie. Nauczyłem się czegoś nowego i interesującego. –

1

TomTom odpowiedział na to pytanie całkiem dobrze, myślę, ale jest jeszcze jedna alternatywa, jeśli znajdziesz się w trudnej sytuacji COM między strukturami. Każde pole można wyrównać samodzielnie przy użyciu atrybutów FieldOffset.

[StructLayout(LayoutKind.Explicit)] 
public struct COMPoint 
{ 
    [FieldOffset(0)] public int X; 
    [FieldOffset(4)] public int Y; 
}