亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

.NET中的字符串在內(nèi)存中的存儲方式

 更新時間:2023年07月17日 09:01:57   作者:Artech  
這篇文章主要介紹了.NET的字符串在內(nèi)存中是如何存儲的,今天來討論下這個問題,對.NET字符串內(nèi)存存儲相關知識感興趣的朋友跟隨小編一起看看吧

毫無疑問,字符串是我們使用頻率最高的類型。但是如果我問大家一個問題:“一個字符串對象在內(nèi)存中如何表示的?”,我相信絕大部分人回答不上來。我們今天就來討論這個問題。

一、字符串對象的內(nèi)存布局

從“值類型”和“引用類型”來劃分,字符串自然屬于引用類型的范疇,所以一個字符串對象自然采用引用類型的內(nèi)存布局。我在很多文章中都介紹過引用類型實例的內(nèi)存布局(《以純二進制的形式在內(nèi)存中繪制一個對象》 和《如何將一個實例的內(nèi)存二進制內(nèi)容讀出來?》,總的來說整個內(nèi)存布局分三塊:ObjHeader + TypeHandle + Payload。對于一般的引用類型實例來說,最后一部分存放的就是該實例所有字段的值,但是字符串有點特別,它有哪些字段呢?

說到這里,可能有人想去反編譯一下String類型,看看它定義了那些字段。其實沒有必要,字符串這個類型有點特別,它的Payload部分由兩部分組成:字符串長度(不是字節(jié)長度)+編碼的文本,下圖揭示了字符串對象的內(nèi)存布局。那么具體采用怎樣的編碼方式呢?可能很多人會認為是UTF-8,實在不然,它采用的是UTF-16,大部分字符通過兩個字節(jié)來表示,少數(shù)的則需要使用四個字節(jié)。至于字節(jié)序,自然是使用小端字節(jié)序。

二、以二進制的方式創(chuàng)建一個String對象

在《以純二進制的形式在內(nèi)存中繪制一個對象》中,我們通過構建一個字節(jié)數(shù)組來表示創(chuàng)建的對象,現(xiàn)在我們依然可以采用類似的方式來創(chuàng)建一個真正的String對象。如下所示的AsString方法用來將用于承載字符串實例的字節(jié)數(shù)組轉換成一個String對象,至于這個字節(jié)數(shù)組的構建,則有CreateString方法完成。CreateString方法根據(jù)指定的字符串內(nèi)容創(chuàng)建一個String對象,并利用輸出參數(shù)返回該對象映射在內(nèi)存中的字節(jié)數(shù)組。

static unsafe string CreateString(string value, out byte[] bytes)
{
    var byteCount = Encoding.Unicode.GetByteCount(value);
    // ObjHeader + TypeHandle + Length + Encoded string
    var size = sizeof(nint) + sizeof(nint) + sizeof(int) + byteCount;
    bytes = new byte[size];
    // TypeHandle
    BinaryPrimitives.WriteInt64LittleEndian(bytes.AsSpan(sizeof(nint)), typeof(string).TypeHandle.Value.ToInt64());
    // Length
    BinaryPrimitives.WriteInt32LittleEndian(bytes.AsSpan(sizeof(nint) * 2), value.Length);
    // Encoded string
    Encoding.Unicode.GetBytes(value).CopyTo(bytes, 20);
    return AsString(bytes);
}
static unsafe string AsString(byte[] bytes)
{
    string s = null!;
    Unsafe.Write(Unsafe.AsPointer(ref s), new IntPtr(Unsafe.AsPointer(ref bytes[8])));
    return s;
}

由于我們需要創(chuàng)建一個字節(jié)數(shù)組來表示String對象,所以必須先計算出這個字節(jié)數(shù)組的長度。我們在上面說過,String類型采用UTF-16/Unicode編碼方式,所以我們調用Encoding.Unicode的GetByteCont方法可以計算出指定的字符串編碼后的字節(jié)數(shù)。在此基礎上我們還需要加上通過一個整數(shù)(sizeof(int))表示字符串長度和TypeHandle(sizeof(nint))和ObjHeader(sizeof(nint),含padding),就是整個String實例在內(nèi)存中占用的字節(jié)數(shù)。

接下來我們填充String類型的TypeHandle的值(String類型方法表地址)、字符串長度和編碼后的字節(jié),最終將填充好的字節(jié)數(shù)組作為參數(shù)調用AsString方法,返回的就是我們創(chuàng)建的String對象。CreateString方法針字符串對象的創(chuàng)建可以通過如下的代碼來驗證。

var literal = "foobar";
string s = CreateString(literal, out var bytes);
Debug.Assert(literal == s);

對于上面定義的AsString方法來說,作為輸入?yún)?shù)的字節(jié)數(shù)組字符串實例的內(nèi)存片段,所以該方法針對同一個數(shù)組返回的都是同一個實例,如下的演示代碼證明了這一點。

var literal = "foobar";
CreateString(literal, out var bytes);
var s1 = AsString(bytes);
var s2 = AsString(bytes);
Debug.Assert(ReferenceEquals(s1,s2));

三、字符串的“可變性”

我們都知道字符串一經(jīng)創(chuàng)建就不會改變,但是對于上面創(chuàng)建的字符串來說,由于我們都將承載字符串實例的內(nèi)存字節(jié)都拿捏住了,那還不是想怎么改就怎么改。比如在如下所示的代碼片段中,我們將同一個字符串的文本從“foo”改成了“bar”。

var literal = "foo";
var s = CreateString(literal, out var bytes);
Debug.Assert(s == "foo");
Encoding.Unicode.GetBytes("bar").CopyTo(bytes, 20);
Debug.Assert(s == "bar");

到此這篇關于.NET的字符串在內(nèi)存中是如何存儲的嗎?的文章就介紹到這了,更多相關.NET字符串內(nèi)存存儲內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

最新評論