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

作为对字符串不变性(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字段之后,如何在具有相同偏移量的字段中得到另一个值?

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

  

我想知道在仅分配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位(即未选中“ Prefer 32-bit”)-实际上,VS中的“首选32位”复选框似乎对Core项目没有任何作用……我必须将平台类型显式设置为x86才能获得Core的32位进程。

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

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

本文链接:https://www.f2er.com/3161973.html

大家都在问