1. The role of sizeof

My personal understanding: sizeof is an operator used to get the memory size of an unmanaged type.

Microsoft's official definition of sizeof:

The sizeof operator returns the number of bytes occupied by a variable of a given type. The argument to the sizeof operator must be the name of a sizeof, or a type parameter qualified to an unmanaged type.

"Microsoft Official Documentation"
https://docs.microsoft.com/en-US/dotnet/csharp/language-reference/operators/sizeof

2.sizeof and Mashal.sizeof

The first thing to say is that sizeof and Marshal.SizeOf are different.

Both sizeof and Marshal.SizeOf in C# can be used to get the size of unmanaged types, but there are differences in functionality and slightly different results.

sizeof can only be used for predefined unmanaged types under normal circumstances, such as int, long, etc. In the unsafe environment, sizeof can be used for value types, but value types cannot have reference types, otherwise the C# compiler will report an error.

Marshal.SizeOf is to get the size of the type by Marshal (conversion, usually translated as a column set, which refers to the conversion of data from one type to another type) to the corresponding unmanaged type. Unlike sizeof, Marshal.SizeOf is allowed on value types that contain reference types.

Reference: "The difference between Marshal.SizeOf and sizeof"

Look at the output first:

 public struct Struct1 {

    public int Id;

}

public struct Struct2 {

    public int Id;
    public string Name;

}

// sizeof()测试 系统内置的基本类型sizeof是固定常量值,编译器自动替换,不是通过计算得到的 不需要用unsafe 自定义的需要unsafe下计算
private void SizeofTest() {
    Console.WriteLine(sizeof(bool));   // 1
    Console.WriteLine(sizeof(byte));   // 1
    Console.WriteLine(sizeof(sbyte));  // 1
    Console.WriteLine(sizeof(short));  // 2
    Console.WriteLine(sizeof(ushort)); // 2
    Console.WriteLine(sizeof(int));    // 4
    Console.WriteLine(sizeof(uint));   // 4
    Console.WriteLine(sizeof(long));   // 8
    Console.WriteLine(sizeof(ulong));  // 8
    Console.WriteLine(sizeof(char));   // 2
    Console.WriteLine(sizeof(float));  // 4
    Console.WriteLine(sizeof(double)); // 8

    //自定义struct
    unsafe {
        Console.WriteLine(sizeof(Struct1));// 4
        //Console.WriteLine(sizeof(Struct2));// 编译器报错 无法获取托管类型("Struct2")的地址和大小,或者声明指向他的指针
    }
}

// Mashal.SizeOf()测试
private void MarshalSizeofTest() {
    // using System.Runtime.InteropServices;
    Console.WriteLine(Marshal.SizeOf(typeof(bool)));   // 4 (sizeof是1)
    Console.WriteLine(Marshal.SizeOf(typeof(byte)));   // 1
    Console.WriteLine(Marshal.SizeOf(typeof(sbyte)));  // 1
    Console.WriteLine(Marshal.SizeOf(typeof(short)));  // 2
    Console.WriteLine(Marshal.SizeOf(typeof(ushort))); // 2
    Console.WriteLine(Marshal.SizeOf(typeof(int)));    // 4
    Console.WriteLine(Marshal.SizeOf(typeof(uint)));   // 4
    Console.WriteLine(Marshal.SizeOf(typeof(long)));   // 8
    Console.WriteLine(Marshal.SizeOf(typeof(ulong)));  // 8
    Console.WriteLine(Marshal.SizeOf(typeof(char)));   // 1 (sizeof是2)
    Console.WriteLine(Marshal.SizeOf(typeof(float)));  // 4
    Console.WriteLine(Marshal.SizeOf(typeof(double))); // 8

    // 自定义struct
    Console.WriteLine(Marshal.SizeOf(typeof(Struct1)));// 4
    Console.WriteLine(Marshal.SizeOf(typeof(Struct2)));// 8
}

3.sizeof size calculation

Before calculating, look at the output in the SizeofTest() method above, and remember the size of the basic data type to use for the calculation.

I personally summarize the following calculation rules based on the output results:

1. First determine the minimum allocation space UnitSize

The maximum space allocated for each unit of a 32-bit application is 32 bits or 8 bytes.

Find the largest value of the basic data type size in the struct member, and the size value of the unit is the smallest between the maxSize value and 8. (int unitSize = Math.Min(maxSize, 8))

2. Allocate a unit space first, and load the member size in the struct in order from top to bottom.

3. The starting sequence number of the space occupied by each type must be 0 or an integer multiple of the size of the type. If the space is not enough, a new unit space will be opened.

for example:

bool occupies 1 byte, and the starting sequence number of the occupied space can be 0, 1, 2, 3, 4....

The short occupies 2 bytes, and the starting sequence number of the occupied space can be 0, 2, 4....

int occupies 4 bytes, and the starting sequence number of the occupied space can be 0, 4....

4. If a member is a custom value type, maxSize needs to be determined by traversing the basic data type size in the value type member, not by the total size of the value type.

The approximate calculation process is as follows:

 // 原文地址:https://www.cnblogs.com/zhangyukof/p/16159965.html
public struct Struct3 {
    public bool a;
    public int b;
    public short c;
}

// 以计算struct3 size为例:

// 1.先获取struct中 成员数据类型size最大的
int maxSize = 4; // bool = 1, int = 4, short = 2

// 2.单元空间大小取成员类型size和8之间的最小值
int unitSize = Math.Min(maxSize, 8); // 4

// 3.开始计算占用空间
(1) □ □ □ □   // 分配一个新单元 4字节

(2) ■ □ □ □   // 装入bool 占用一个字节

(3) ■ □ □ □   // 剩余空间序号不满足0或4的整数倍 不能装入int 重新分配一个单元
    □ □ □ □

(4) ■ □ □ □   // 装入int 从新单元的0位置开始占用4个字节
    ■ ■ ■ ■

(5) ■ □ □ □   // 分配一个新单元 4字节
    ■ ■ ■ ■
    □ □ □ □

(6) ■ □ □ □   // 装入short 占用2个字节
    ■ ■ ■ ■
    ■ ■ □ □

// 4.共计3个单元 3*unitSize = 12字节

4. Member order affects the size of sizeof

Since the allocation rule 3 is that the starting sequence number of the space occupied by each type must be 0 or an integer multiple of the size of the type, if the space is not enough, a new unit space will be opened.

Reallocating a new unit starts at position 0, so the order of the members may affect the size of sizeof.

For example, if the order of b and c in struct3 is reversed, it becomes 8 bytes.

 public struct Struct3 {
    public bool a;
    public short c;
    public int b;
}

(1) □ □ □ □   // 分配一个新单元 4字节

(2) ■ □ □ □   // 装入bool 占用一个字节

(3) ■ □ ■ ■   // 装入short 占用2个字节 (short起始序号为2)

(4) ■ □ ■ ■   // 分配一个新单元 4字节
    □ □ □ □

(5) ■ □ ■ ■   // 装入int 占用4个字节
    ■ ■ ■ ■

// 共计2个单元 2*unitSize = 8字节

Yes, this is a written test site.

5. Custom value types affect the size of sizeof

Another harder one, when there is a member type that is a custom type and the total size exceeds 8, let's see what the result is:

Calculate the size of the custom value type Struct4 size:

 public struct Struct4 {

    public bool a;    // size = 1
    public StructSize16 b;    // size = 16
    public int c;     // size = 4
    public short d;   // size = 2

}

public struct StructSize16 {

    public int a1; // size = 4
    public int a2;
    public int a3;
    public int a4;

}

If calculated as follows:

 int maxSize = 16; // StructSize16 = 16
int unitSize = Math.Min(maxSize, 8); // 8

1.■ □ □ □ □ □ □ □// 分配一个单元8字节 装入bool 占1字节

2.■ □ □ □ □ □ □ □ // 剩余空间的起始序号不满足StructSize16的需求 分配2个新单元 装入StructSize16 占16个字节
  ■ ■ ■ ■ ■ ■ ■ ■
  ■ ■ ■ ■ ■ ■ ■ ■
  
3.■ □ □ □ □ □ □ □ // 分配一个单元8字节 装入int 占4字节
  ■ ■ ■ ■ ■ ■ ■ ■
  ■ ■ ■ ■ ■ ■ ■ ■
  ■ ■ ■ ■ □ □ □ □
  
4.■ □ □ □ □ □ □ □ // 装入short 占2字节
  ■ ■ ■ ■ ■ ■ ■ ■
  ■ ■ ■ ■ ■ ■ ■ ■
  ■ ■ ■ ■ ■ ■ □ □
  
5.共计4个单位空间  4*unitSize = 32字节

The result is wrong, because the member encounters a custom type and needs to traverse the basic data type in the custom type and then determine the maxSize

There are 4 members in the custom value type. The maxSize is 4. The following should be noted here. The correct calculation method should be as follows:

 int maxSize = 4; // bool = 1, short = 2, int = 4
int unitSize = Math.Min(maxSize, 8); // 4

1.■ □ □ □ // 分配一个单元4字节 装入bool 占1字节

2.■ □ □ □ // 剩余3字节的空间装不下StructSize16 分配4个新单元 装入StructSize16 占16个字节
  ■ ■ ■ ■
  ■ ■ ■ ■
  ■ ■ ■ ■
  ■ ■ ■ ■
  
3.■ □ □ □ // 分配一个新单元4字节 装入int 占4字节
  ■ ■ ■ ■
  ■ ■ ■ ■
  ■ ■ ■ ■
  ■ ■ ■ ■
  ■ ■ ■ ■

4.■ □ □ □ // 分配一个新单元4字节 装入short 占2字节
  ■ ■ ■ ■
  ■ ■ ■ ■
  ■ ■ ■ ■
  ■ ■ ■ ■
  ■ ■ ■ ■
  ■ ■ □ □
  
5.共计7个单位空间  7*unitSize = 28字节

This calculation result is consistent with the output result, both of which are 28 bytes.

The result of replacing the custom type StructSize16 with decimal is also 28 bytes, because decimal also has 4 members of type int. From the result, decimal is not regarded as a basic data type, and maxSize is 4 not 16.

Finally, define a slightly more complex type to test whether the calculation rules are correct:

 public struct Student {

    public int num;
    public bool isAtSchool;
    public short age;
    public long id;
    public byte rank;

}

int maxSize = 8; // bool = 1, byte = 1, short = 2, int = 4, long = 8
int unitSize = Math.Min(maxSize, 8); // 8

1.□ □ □ □ □ □ □ □ // 分配一个单元8字节

2.■ ■ ■ ■ □ □ □ □ // 装入int 占4字节

3.■ ■ ■ ■ ■ □ □ □ // 装入bool 占1字节

4.■ ■ ■ ■ ■ □ ■ ■ // 装入short 占2字节 起始序号为6

5.■ ■ ■ ■ ■ □ ■ ■ // 分配一个新单位8字节
  □ □ □ □ □ □ □ □
  
6.■ ■ ■ ■ ■ □ ■ ■ // 装入long 占8字节
  ■ ■ ■ ■ ■ ■ ■ ■
  
7.■ ■ ■ ■ ■ □ ■ ■ // 新分配一个单位空间8字节
  ■ ■ ■ ■ ■ ■ ■ ■
  □ □ □ □ □ □ □ □
  
8.■ ■ ■ ■ ■ □ ■ ■ // 装入byte 占1字节
  ■ ■ ■ ■ ■ ■ ■ ■
  ■ □ □ □ □ □ □ □
  
9.共计 3*8 = 24字节 // 计算结果与输出结果一致 都是24字节 计算正确

// 可以看到空间第三行首位单独占了一个字节,空余7个字节,
// 如果把第三行的数据移动到第一行空白的位置,那么就不需要第三行了,可以节省空间,试一下。
// 把Student中rank字段的位置移动到isAtSchool后面。

public struct Student {

    public int num;
    public bool isAtSchool;
    public byte rank;
    public short age;
    public long id;

}

// 这样内存占用就变成了:
■ ■ ■ ■ ■ ■ ■ ■
■ ■ ■ ■ ■ ■ ■ ■

// 理论上应该16个字节就够用了,运行测试代码输出一下结果,sizeof(Student)确实是16,检验成功。

Summarize:

The size of the unmanaged type calculated by sizeof() is not directly added up according to the size of the member data type, it involves some special calculation rules, and normal use does not need to remember these complicated things.

If you need to use this part of the knowledge, it should involve performance optimization or encounter test questions, remember that the size of the basic data type can meet the general needs.

The order of members within a struct affects the sizeof size member variable also affects the sizeof size. Unnecessary variables should not be defined as member variables, private will also be calculated into the size, and members should be defined with a smaller numerical size as much as possible.

Finally, there is a method to view the memory arrangement of custom struct:

I'm using Visual Studio software, open Debug > Window > Memory > Memory 1.

The breakpoint is set at the location where the object is declared, and the memory address bar is entered & the object name when the break is made.

Assign the maximum value to the members of the object one by one, and you can see the memory changes when F10 goes down.

At this point, the calculation process of sizeof comes to an end.

PS: Please indicate the original address for reprint: https://www.cnblogs.com/zhangyukof/p/16159965.html

References:

"The difference between C# Marshal.SizeOf and sizeof"
"C# sizeof usage"


冰封百度
233 声望43 粉丝

Unity游戏程序员一枚。生命不息,学习不止。