9 c#
为什么尺寸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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
输出是:
8
12
Run Code Online (Sandbox Code Playgroud)
字段之间有一些填充.使用先前字段和下一字段计算填充.
此外,这种情况应该是真的:
(size of struct) % (size of largest type) == 0
Run Code Online (Sandbox Code Playgroud)
在您的情况下,最大的类型是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
}
Run Code Online (Sandbox Code Playgroud)
如果你想要两个结构相同的大小,你可以使用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;
}
Run Code Online (Sandbox Code Playgroud)
要么
你可以使用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;
}
Run Code Online (Sandbox Code Playgroud)
此外,您可以获得结构大小,而无需unsafe:
Console.WriteLine(System.Runtime.InteropServices.Marshal.SizeOf(typeof(A)));
Console.WriteLine(System.Runtime.InteropServices.Marshal.SizeOf(typeof(B)));
Run Code Online (Sandbox Code Playgroud)
这是因为您的编译器保留在成员之间插入填充的权限struct,以及最后的一些空间.(但是请注意,填充,不会在第一构件之前允许的.)
这样做是为了使成员的开始在易于寻址的内存位置上对齐.
特别是,编译器可能在单个char和一个之间插入填充int.偶数个char接着一个s int可能因此占用较小的空间比一个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.除了类和结构之外,静态变量以及方法的参数和局部变量也需要这种对齐保证.但几乎不容易观察到.
| 归档时间: |
|
| 查看次数: |
853 次 |
| 最近记录: |