← 全部文章 计算机基础

在 VS 内存窗口里看 C# 变量

2022 年 1 月 14 日 · 1 分钟读完

目录
  1. 先打开不安全代码
  2. 拿到地址,看内存
  3. 用指针读和改
  4. 延伸

前几篇讲的补码、浮点数、字节序,都可以在 VS 里亲眼看到。这一篇记录怎么看。

先打开不安全代码

指针只能写在 unsafe 块里,需要在项目属性里打开:项目 → 属性 → 生成 → 勾选「允许不安全代码」。SDK 风格的项目也可以直接在 .csproj 里加 <AllowUnsafeBlocks>true</AllowUnsafeBlocks>。

拿到地址,看内存

static void Main(string[] args)
{
    int bst = 270;
    unsafe
    {
        char* cp = (char*)&bst;
    } // 断点打在这
}

断点停下后,在监视窗口里看 cp 就能得到地址,把地址粘到 调试 → 窗口 → 内存 里:

270 的十六进制是 0x010E,内存里却是 0E 01 00 00:低位字节在前,这就是上一篇讲的小端序。x86 和 ARM 上的 Windows 都是小端。

用指针读和改

static void Main(string[] args)
{
    int bst1 = 99999;
    short bst2 = 9999;
    short bst3 = 9984;

    unsafe
    {
        char* cp = (char*)&bst1;
        Console.WriteLine(Convert.ToString(*cp, 16)); // 869f
        *(cp + 1) = (char)34;                          // 改掉高 16 位
        Console.WriteLine(bst1);                       // 2262687

        char* cp2 = (char*)&bst2;
        Console.WriteLine(Convert.ToString(*cp2, 16)); // 270f
        *cp2 = (char)bst3;
        Console.WriteLine(Convert.ToString(*cp2, 16)); // 2700
        Console.WriteLine(bst2);                       // 9984
    }
}

  • 99999 是 0x0001869F,但 *cp 只打印出 869F:因为 C# 的 char 占 2 个字节,char* 一次读 16 位。
  • cp + 1 不是往后挪 1 个字节,而是挪一个 char,也就是 2 个字节,正好指向高 16 位。把它改成 34(0x22)后,整个数变成 0x0022869F = 2262687。

指针加 1 移动的字节数等于它指向的类型的大小,这一点和 C 语言一样。

延伸

  • 想按字节看,用 byte*:byte* p = (byte*)&bst; 之后 p[0] 就是最低字节 0x0E。用 char* 容易误会成「地址以 16 位为单位」,其实只是 char 有 2 字节。
  • 不写 unsafe 也能看字节:BitConverter.GetBytes(270) 返回 { 0x0E, 0x01, 0x00, 0x00 };BitConverter.IsLittleEndian 告诉你当前机器的字节序。.NET Core 2.1 起还有 MemoryMarshal.AsBytes(new Span<int>(ref bst)) 这类零拷贝的写法。
  • 和 PLC 打交道一定会遇到字节序:西门子 S7 是大端,上位机收到 00 01 86 9F 要先反转再转 int,或者直接用 BinaryPrimitives.ReadInt32BigEndian。见 西门子 PLC 通讯(一):不同数据的存储方式。
  • fixed:数组和字符串在托管堆上,GC 可能挪动它们,取地址前要用 fixed (byte* p = array) { ... } 把它钉住。

# C# # 内存 # 指针

原文首发于 blog.csdn.net