【问题标题】:Why the size of struct A is not equal size of struct B with same fields?为什么结构 A 的大小不等于具有相同字段的结构 B 的大小?
【发布时间】:2017-07-15 06:30:33
【问题描述】:

为什么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

【问题讨论】:

标签: c#


【解决方案1】:

字段之间有一些填充。使用前一个字段和下一个字段计算填充。

另外,这个条件应该为真:

(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.Sequential、Pack = 1 和CharSet = 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)));

【讨论】:

  • @seyxsultan,我不知道什么是最好的方法,但是LayoutKind.Sequential 我得到不同的尺寸。
  • @seyxsultan,你买多大的?如果你得到大小 6 - 这是错误的大小 - 你有两个 char 变量,每个 2 字节和 int 变量,大小是 2+2+4=8
  • 我都得到 8
  • @seyxsultan,我得到了size == 6,但无论如何,如果它对你有用,那就太好了,谢谢你的回复
  • [StructLayout(LayoutKind.Sequential, Pack = 1)] struct A { char a;字符 c;诠释 b; } [StructLayout(LayoutKind.Sequential, Pack = 1)] 结构 B { char a;诠释 b;字符 c; }
【解决方案2】:

这是一个处理器实现细节,.NET 极力隐藏。变量需要有一个存储位置,允许处理器通过单个数据总线操作读取和写入值。这使得变量地址的对齐非常重要。读取单个字节从来都不是问题。但是 short(2 字节)的地址应该是 2 的倍数。int(4 字节)的地址应该是 4 的倍数。理想情况下,long 或 double(8 字节)的地址应该是8 的倍数,但在 32 位处理器上并不总是能做到这一点。

与 RISC 内核不同,Intel 和 AMD 处理器允许不对齐的读取和写入。但这可能是有代价的,它可能需要两个数据总线周期来读取两个字节块,一个值的部分高字节和部分低字节。使用将这些字节洗牌到正确位置的电路​​。这需要时间,通常是额外的 1 到 3 个时钟周期。在 RISC 内核上花费大量时间来处理总线错误陷阱。

但更严重的是,它破坏了 .NET 内存模型。它为简单的值类型和对象引用提供了原子性保证。未对齐的读取和写入打破了这一承诺。它可能会导致撕裂,观察正在写入的部分字节。更糟糕的是,它会破坏垃圾收集器。 GC 严重依赖于以原子方式更新的对象引用。

所以当 CLR 确定结构或类的布局时,它必须确保满足对齐要求。如果不是,那么它需要在变量之间留下额外的未使用空间。最后可能还有额外的空间,以确保成员在存储在数组中时仍然对齐。该额外空间的通用词是“填充”。

特定于一个类的声明,它有[StructLayout(LayoutKind.Auto)],它可以将成员打乱,以达到最好的布局。不是结构,默认情况下它们是 LayoutKind.Sequential 。除了类和结构之外,静态变量以及方法的参数和局部变量也需要这种对齐保证。但几乎没有那么容易观察到。

【讨论】:

    【解决方案3】:

    这是因为您的编译器保留在struct 的成员之间插入填充的权利,并在末尾加上一些空格。 (但请注意,在第一个成员之前不允许填充。)

    这样做是为了在易于寻址的内存位置上对齐成员的开头。

    特别是,编译器可能会在单个 char 和 int 之间插入填充。因此,偶数个chars 后跟一个int 占用的空间可能比char 后跟一个int 后跟奇数个chars 占用的空间少。

    【讨论】:

    • 我相信这取决于特定的 C# 实现。 (对于许多编译器而言,C 和 C++ 中存在等效功能是值得的,尽管这不是这些语言的标准要求的一部分。)。也许问这个问题更有针对性?
    【解决方案4】:

    字段顺序不同;我猜想在填充成员时大小会有所不同(即,以这样一种方式定位,即它们以偶数机器字开头,以便以内存消耗为代价使访问更容易)。

    【讨论】:

      猜你喜欢
      • 2011-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-17
      • 1970-01-01
      • 2016-08-13
      相关资源
      最近更新 更多