簡體   English   中英

為什么struct A的大小不等於具有相同字段的struct B的大小?

[英]Why the size of struct A is not equal size of struct B with same fields?

為什么struct A的大小不等於struct B大小?

而我需要做的是,它們的尺寸相同嗎?

using System;

namespace ConsoleApplication1
{
    class Program
    {
        struct A
        {
            char a;
            char c;
            int b;
        }

        struct B
        {
            char a;
            int b;
            char c;

        }


        static void Main(string[] args)
        {
            unsafe
            {
                Console.WriteLine(sizeof(A));
                Console.WriteLine(sizeof(B));
            }
            Console.ReadLine();
        }
    }
}

輸出是:

8
12

字段之間有一些填充。 使用先前字段和下一字段計算填充。

此外,這種情況應該是真的:

(size of struct) % (size of largest type) == 0

在您的情況下,最大的類型是int ,其大小是4個字節。

struct A
{
    char a; // size is 2, no previous field, next field size is 2 - no alignment needed
    char c; // size is 2, previous size is 2 -> 2 + 2 = 4, next size is 4 - no alignment needed
    int b;  //size is 4, it is last field, size is 4 + 4 = 8.  

    //current size is 2 + 2 + 4 = 8
    //8 % 4 == 0 - true - 8 is final size
}

struct B
{
    char a; // size is 2, next size is 4, alignment needed - 2 -> 4, size of this field with alignment is 4
    int b;  // size is 4, previous is 4, next size is 2(lower) - no alignment needed
    char c; // size is 2, previous is 4 + 4 = 8 - no alignment needed

    //current size is 4 + 4 + 2 = 10
    //but size should be size % 4 = 0 -> 10 % 4 == 0 - false, adjust to 12
}

如果你想要兩個結構相同的大小,你可以使用LayoutKind.Explicit

[StructLayout(LayoutKind.Explicit)]
public struct A
{
    [FieldOffset(0)]
    char a;

    [FieldOffset(2)]
    char c;

    [FieldOffset(4)]
    int b;
}

[StructLayout(LayoutKind.Explicit)]
public struct B
{
    [FieldOffset(0)]
    char a;

    [FieldOffset(2)]
    int b;

    [FieldOffset(6)]
    char c;
}

要么

您可以使用LayoutKind.SequentialPack = 1CharSet = CharSet.Unicode來獲取大小8。

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
public struct A
{
    char a;
    char c;
    int b;
}

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
public struct B
{        
    char a;
    int b;
    char c;
}

此外,您可以獲得沒有unsafe結構大小:

Console.WriteLine(System.Runtime.InteropServices.Marshal.SizeOf(typeof(A)));
Console.WriteLine(System.Runtime.InteropServices.Marshal.SizeOf(typeof(B)));

這是因為您的編譯器保留在struct成員之間插入填充的權限,以及最后的一些空格。 (但是請注意,填充, 不是第一個成員之前允許的。)

這樣做是為了使成員的開始在易於尋址的內存位置上對齊。

特別是,編譯器可能在單個charint之間插入填充。 因此,后跟int的偶數個char可能占用的空間少於char后跟一個int后跟奇數個char的空間。

這是一個處理器實現細節,.NET非常難以隱藏。 變量需要一個存儲位置,允許處理器通過單個數據總線操作讀取和寫入值。 這使得變量地址的對齊非常重要。 讀取單個字節絕不是問題。 但是短(2個字節)的地址應該是2的倍數.int(4個字節)的地址應該是4的倍數。理想情況下,long或double(8個字節)的地址是a 8的倍數,但不能總是實現,而不是在32位處理器上。

與RISC內核不同,英特爾和AMD處理器允許不對齊的讀寫操作。 但這可能需要付出代價,可能需要兩個數據總線周期才能讀取兩個字節塊,一部分是字節的高位字節,另一部分是低位字節。 使用將這些字節混合到正確位置的電路​​。 這需要時間,通常需要額外的1到3個時鍾周期。 RISC內核上有很多時間來處理總線錯誤陷阱。

但更嚴重的是,它破壞了.NET內存模型。 它為簡單的值類型和對象引用提供了原子性保證。 未對齊的讀寫會破壞這一承諾。 它可能會導致撕裂 ,觀察正在寫入的部分字節。 更糟糕的是,它可以打破垃圾收集器。 GC主要依賴於原子更新的對象引用。

因此,當CLR確定結構或類的布局時,它必須確保滿足對齊要求。 如果不是那么它需要在變量之間留出額外的未使用空間。 並且最后可能有額外的空間來確保成員在存儲在數組中時仍然對齊。 額外空間的通用詞是“填充”。

具體到類聲明,它有[StructLayout(LayoutKind.Auto)],它可以隨機播放成員以實現最佳布局。 不是結構,默認情況下它們是LayoutKind.Sequential。 除了類和結構之外,靜態變量以及方法的參數和局部變量也需要這種對齊保證。 但幾乎不容易觀察到。

字段的順序是不同的; 我猜測大小是不同的,因為成員被填充(即,它們以一個偶數機器字開始的方式,以便以內存消耗為代價使訪問更容易)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM