为什么在C#中使用FieldOffset(0)最终会为char数组和字符串使用不同的指针?

问题描述 投票:1回答:1

作为对字符串不变性(https://stackoverflow.com/a/37253663/6619353)的好答案的后续,我已经开始尝试使用这种技术来理解可修改字节的偏移量。

最后,我发现对两个引用字段使用[FieldOffset(0)]不会使指针具有相同的值。

这里是测试:

using System;
using System.Runtime.InteropServices;

namespace ConsoleApp
{
    [StructLayout(LayoutKind.Explicit)]
    public struct MutableString
    {
        [FieldOffset(0)] 
        public readonly string AsString;

        [FieldOffset(0)] 
        public readonly char[] AsCharArray;

        public MutableString(string original)
        {
            AsCharArray = null;
            AsString = original;
        }
    }

    public static class Program
    {
        public static unsafe void Main(string[] args)
        {
            var mutableString = new MutableString("test");

            fixed (char* pString = mutableString.AsString, pCharArray = mutableString.AsCharArray)
            {
                Console.WriteLine((long)pString);    // 2229380919860
                Console.WriteLine((long)pCharArray); // 2229380919864
            }
        }
    }
}

上面的代码打印不同的数字(当然,确切的值有时会有所不同。

差异总是4个字节(2个字符)。

这里是csproj文件:

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFrameworks>netcoreapp2.0;net47</TargetFrameworks>
        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    </PropertyGroup>
</Project>

。NET Core dll和net47 exe,调试/发布,x86 / x64构建配置的行为是相同的。

主机是Win10 x64。

我想知道在仅分配AsString字段后,如何在具有相同偏移量的字段中得到another值?

c# .net clr unsafe
1个回答
1
投票

我想知道在仅分配AsString字段之后,如何在具有相同偏移量的字段中获得另一个值?

您不是。

如果将两个字段的值与object.ReferenceEquals(mutableString.AsString, mutableString.AsCharArray)进行比较,则会发现两个字段相等,正如预期的那样。

让您烦恼的是从stringchar[]类型到指针的隐式转换。这两种类型都是托管类型,因此fixed语句必须固定对象并返回指向char数据的适当指针。这是错误的转换,而不是结构中实际存储的值。

关于转换出错的原因,是由于padding differences in arrays between 64-bit and 32-bit processes。 .NET Framework(台式机)和Core之间存在差异,因为默认项目设置不同:台式机默认情况下首选32位,而Core默认情况下不首选32位(即未选中“首选32位”)-实际上,VS中的“首选32位”复选框对于Core项目似乎没有任何作用……我必须将平台类型显式设置为x86才能获得Core的32位进程。]

char[]到指针的隐式转换期望填充的额外4个字节。但是,由于您的引用实际上不是对char[]对象的引用,而是对string对象的引用,因此实际上不存在填充,因此指针的末尾超出了4个字节。

考虑到当重新解释为对string对象的引用时,确实没有理由期望对char[]对象的引用是有效的—对象布局在32位进程中是巧合兼容的,但这不是问题。 .NET规范承诺(这是实现的细节)-我认为这是“合理的”。如果要创建有效的联合数据结构,则必须设置自己的保护措施以确保仅将联合字段解释为实际设置的字段。

© www.soinside.com 2019 - 2024. All rights reserved.