You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

Marshal.SizeOf与sizeof差异困惑:blittable结构体非托管内存计算疑问

为啥Marshal.SizeOf结果和sizeof不一样,而且非托管内存越界写没报错?

这个问题其实戳中了.NET里托管内存与非托管内存、类型封送的几个关键差异,咱们一步步拆解:

1. sizeof和Marshal.SizeOf根本不是一回事

先搞明白这俩方法的本质:

  • sizeof(TestStruct):计算的是托管堆里这个结构体实际占用的内存大小。C#里的char是固定的UTF-16编码(2字节),你的结构体包含两个char,加起来就是4字节,这个结果完全符合预期。
  • Marshal.SizeOf(typeof(TestStruct)):计算的是**.NET把这个结构体封送到非托管内存时的默认尺寸**。默认情况下,.NET会把C#的char映射成非托管的ANSI字符(1字节),所以两个char的封送大小就是2字节。这不是结构体本身的内存大小,只是封送器默认规则下的非托管表示大小。

要是你想让Marshal.SizeOf返回和sizeof一致的4字节,给结构体加个特性就行:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct TestStruct { public char x; public char y; }

这样强制封送时使用UTF-16宽字符,Marshal.SizeOf就会返回4了。

2. 非托管内存越界写没报错?那是你运气好!

你用Marshal.AllocHGlobal只分配了2字节,却硬塞进去两个2字节的char,这妥妥是内存越界访问,属于不安全代码里的未定义行为:

  • 非托管堆没有CLR那样严格的边界检查,它是直接调用系统API分配的内存,越界写入时,只要那块内存还属于当前进程(没被系统回收或分配给别的进程),就不会立刻触发访问冲突。
  • 你这次没报错,只是刚好越界的那块内存暂时是空闲的,没被其他数据占用。但这绝对不是“正常工作”,要是后续这块内存被分配给别的变量使用,直接就会破坏数据,轻则程序崩溃,重则出现各种诡异的bug,排查起来极其困难。

说白了,这种写法是严重错误的,绝对不能这么干!


内容的提问来源于stack exchange,提问作者oliver

火山引擎 最新活动