关于c#:C-sizeof-计算规则

5次阅读

共计 5721 个字符,预计需要花费 15 分钟才能阅读完成。

1.sizeof 的作用

我集体的了解:sizeof 是用来获取非托管类型所占内存大小的操作符。

微软官网对 sizeof 的定义:

sizeof 运算符返回给定类型的变量所占用的字节数。sizeof 运算符的参数必须是一个 sizeof 的名称,或是一个限定为非托管类型的类型参数。

《微软官网文档》
https://docs.microsoft.com/zh…

2.sizeof 和 Mashal.sizeof

首先要说的是 sizeof 和 Marshal.SizeOf 是有差别的。

C# 中的 sizeof 和 Marshal.SizeOf 都能够用来获取非托管类型的大小,然而性能上有差异,后果也略有差别。

sizeof 在失常环境下只能用于预约义的非托管类型,如 int、long 等等。在 unsafe 环境下,sizeof 能够被用于值类型,然而值类型中不能够有援用类型,否则 C# 编译器会报错。

Marshal.SizeOf 则是取得该类型被 Marshal(转换,通常翻译为列集,指数据从一种类型转换到另外一种类型)到对应的非托管类型的大小。和 sizeof 不同,Marshal.SizeOf 容许用在含有援用类型的值类型上。

参考资料:《Marshal.SizeOf 和 sizeof 的区别》

先看输入后果:

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 大小的计算

在计算之前,请看下面 SizeofTest()办法内的输入后果,记住根本数据类型的大小,用来计算。

我集体依据输入后果总结了以下几个计算规定:

1. 首先确定最小调配空间 UnitSize

32 位利用每一个单位调配的最大空间为 32 位即 8 字节。

找到 struct 成员中根本数据类型 size 最大的值,单元的 size 值在该 maxSize 值和 8 之间取最小。(int unitSize = Math.Min(maxSize, 8))

2. 先调配一个单元空间, 从上往下按程序装 struct 中的成员 size。

3. 每个类型占用空间的起始序号必须为 0 或该类型 size 的整数倍,空间不够就从新开一个单位空间。

比方:

bool 占 1 字节,占用空间的起始序号能够为 0,1,2,3,4…。

short 占 2 字节,占用空间的起始序号能够为 0,2,4 ….。

int 占 4 字节,占用空间的起始序号能够为 0,4….。

4. 如果某个成员是自定义的值类型, maxSize 须要遍历值类型成员中的根本数据类型 size 决定,不是由值类型的总 size 决定。

大略计算过程如下:

// 原文地址: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. 成员程序影响 sizeof 的大小

因为调配的规定 3 是每个类型占用空间的起始序号必须为 0 或该类型 size 的整数倍,空间不够就从新开一个单位空间。

重新分配一个新的单元从 0 地位开始装,所以成员的程序有可能影响 sizeof 的大小。

比方把 struct3 中 b 和 c 的程序调换一下就变成了 8 字节

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 字节

没错,这是一个口试考点。

5. 自定义值类型影响 sizeof 的大小

再来一个难一点的,当有成员类型是自定义类型 并且总 size 超过 8 看看后果如何:

计算自定义值类型 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;

}

如果依照以下形式计算:

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 字节

后果就错了,因为成员遇到自定义类型要遍历自定义类型里的根本数据类型再确定 maxSize

自定义值类型外面的成员有 4 个 都是 int 型 maxSize 为 4,这里要留神以下,正确计算形式应该如下:

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 字节

这个计算结果与输入后果保持一致,都是 28 字节。

把自定义类型 StructSize16 换成 decimal 后果也是 28 字节,因为 decimal 外面也有 4 个 int 类型的成员,从后果看来,decimal 没有被当成根本数据类型来对待,maxSize 是 4 不是 16。

最初再定义一个略微简单点的类型来测验一下计算规定是否正确:

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, 测验胜利。

总结:

sizeof()计算非托管类型的大小不是间接依照成员数据类型大小加起来的,波及到一些非凡的计算规定,失常应用不须要记这些简单的货色。

如果须要用到这部分常识了,应该波及到性能优化或遇到考题了,记住根本数据类型的 size 就能够满足个别需要了。

struct 内成员的程序会影响 sizeof 大小 成员变量也会影响 sizeof 大小。没必要的变量不要定义为成员变量,private 也会被计算进 size 内,成员尽量用数值 size 更小的定义。

最初再附赠一个查看自定义 struct 内存排布的办法:

我用的是 Visual Studio 软件,关上调试 > 窗口 > 内存 > 内存 1。

断点设置在申明对象的地位,在中断的时候内存地址栏输出 & 对象名称。

给对象的成员一一赋值成最大值,F10 往下走就能够看到内存变动了。

至此,sizeof 的计算过程告一段落。

PS: 转载请表明原文地址:https://www.cnblogs.com/zhang…

参考资料:

《C# Marshal.SizeOf 和 sizeof 的区别》
《C#sizeof 用法》

正文完
 0