C#中结构体的内存大小与分配问题~求解

时间:2021-08-22 02:30:11
在与非托管代码的互操作时,有时会需要结构体传递结构体参数,因为两者之间存在内存分配上的差异,而坚决这个差异必须知道C#中的结构体内存分配与大小。
实验场景:
所有结构体均使用默认的: 
StructLayoutAttribute(Value = LayoutKind.Sequential,Pack = 8)

结构体实际大小计算使用: 
Marshal.SizeOf(typeof(StructName))


*下面的现象和问题假设在试验场景所用方法都正确下进行,如以上方式有误,请告知

先说说我的理解,结构体中的字段因为需要与Pack大小对齐,所以每次需保证字段的起始地址是Pack大小的整数倍。
//16字节
struct S1
{
sbyte b; //1
long l;  //8
}


//16字节
struct S11
{
long l;  //8
sbyte b; //1
}

因为long需要8个字节,而前面的sbyte只暂用1个字节,这时候需要额外的浪费7个字节使l字段偏移位置刚好在pack大小的倍数上。

但是下面的2个结构体,成员是一样的(除了顺序),但是大小却是不一样的。
struct S2 //32字节
        {
            int x;    //4 
            double b; //8
            double c; //8
            byte a;   //1
        }

        struct S3 //24字节
        {
            byte a;   //1
            int x;    //4 
            double b; //8
            double c; //8
        }

问题1:猜想是a字段与x字段共同占用了第一个pack大小的空间。那么这与字段偏移量是pack大小的整数倍不就相矛盾了?求解释

还有一个现象:
struct S4 //12
{
   sbyte s;//1
   int i1; //4
   int i2; //4
}

问题2:假设字段s与字段i1合并,那么根据前面的结构体S11,应该是16字节。字段i2(相对S11中的字段b)不足pack大小(8字节),补足。但实际为什么事12字节?求解释



5 个解决方案

#1


附上摘自C# 4.0 in nutshell中的一段描述,一直无法理解,很纠结。
For access speed, each field is placed at an offset that is a multiple of the field’s size.
That multiplier, however, is restricted to a maximum of x bytes, where x is the pack
size. In the current implementation, the default pack size is 8 bytes, so a struct comprising
a sbyte followed by an (8-byte) long occupies 16 bytes, and the 7 bytes following
the sbyte are wasted.

#2


和系统有关吧
X86 系统最小分配内存为 4字节,就是Int32

可以用sizeof() 算一下结构体的长度

#4


字节对齐
连续分配,不足8字节的对齐到8字节,如果剩下不足存放下一个变量,则补齐到8字节,下一个8字节开始保存。


struct S2 //32字节
{
int x;    //4 :0~3
double b; //8 :8-4=4,不足以保存double的8字节,所以此处浪费4字节,double b位置:8~16
double c; //8 :17~24
byte a;   //1 :25,但根据字节对齐原则,后面7个字节留空
}

struct S3 //24字节
{
byte a;   //1 :0
int x;    //4 :1~4
double b; //8 :8-5不足以保存double,则浪费3字节,double b位置:8~16
double c; //8 :17~24
}

#5


引用 4 楼 wuyazhe 的回复:
字节对齐
连续分配,不足8字节的对齐到8字节,如果剩下不足存放下一个变量,则补齐到8字节,下一个8字节开始保存。

C# code

struct S2 //32字节
{
    int x;    //4 :0~3
    double b; //8 :8-4=4,不足以保存double的8字节,所以此处浪费4字节,double b位置:8~16
    double c; //8 :……

但是问题中的S4结构体,却是12字节的,按照补足原则应该是16字节吧(Pack为8字节下)。

#1


附上摘自C# 4.0 in nutshell中的一段描述,一直无法理解,很纠结。
For access speed, each field is placed at an offset that is a multiple of the field’s size.
That multiplier, however, is restricted to a maximum of x bytes, where x is the pack
size. In the current implementation, the default pack size is 8 bytes, so a struct comprising
a sbyte followed by an (8-byte) long occupies 16 bytes, and the 7 bytes following
the sbyte are wasted.

#2


和系统有关吧
X86 系统最小分配内存为 4字节,就是Int32

可以用sizeof() 算一下结构体的长度

#3


#4


字节对齐
连续分配,不足8字节的对齐到8字节,如果剩下不足存放下一个变量,则补齐到8字节,下一个8字节开始保存。


struct S2 //32字节
{
int x;    //4 :0~3
double b; //8 :8-4=4,不足以保存double的8字节,所以此处浪费4字节,double b位置:8~16
double c; //8 :17~24
byte a;   //1 :25,但根据字节对齐原则,后面7个字节留空
}

struct S3 //24字节
{
byte a;   //1 :0
int x;    //4 :1~4
double b; //8 :8-5不足以保存double,则浪费3字节,double b位置:8~16
double c; //8 :17~24
}

#5


引用 4 楼 wuyazhe 的回复:
字节对齐
连续分配,不足8字节的对齐到8字节,如果剩下不足存放下一个变量,则补齐到8字节,下一个8字节开始保存。

C# code

struct S2 //32字节
{
    int x;    //4 :0~3
    double b; //8 :8-4=4,不足以保存double的8字节,所以此处浪费4字节,double b位置:8~16
    double c; //8 :……

但是问题中的S4结构体,却是12字节的,按照补足原则应该是16字节吧(Pack为8字节下)。