C 中如何正確的操作字串?

2022-07-20 08:24:13 字數 4592 閱讀 1686

字串應該是所有程式語言中使用最頻繁的一種基礎資料型別。如果使用不慎,我們就會為一次字串的操作所帶來的額外效能開銷而付出代價。本條建議將從兩個方面來**如何規避這類效能開銷:

1. 確保盡量少的裝箱

2. 避免分配額外的記憶體空間。

第乙個方面:確保盡量少的裝箱

對於裝拆箱,我們應該不陌生,值型別轉換成引用型別即為裝箱, 引用型別轉換成值型別即為拆箱。 在自己編寫的**中,應當盡可能的避免編寫不必要的裝箱**。裝箱之所以會帶來效能損耗,因為它需要完成下面三個步驟:

• 首先,會為值型別在託管堆中分配記憶體。除了值型別本身所分配的記憶體外,記憶體總量還要加上型別物件指標和同步塊索引所占用的記憶體。

• 然後,將值型別的值複製到新分配的堆記憶體中。

• 最後,返回已經成為引用型別的物件的位址。

下面是一行最簡單的裝箱**

1

object obj = 1;

這行語句將整型常量1賦給object型別的變數obj; 眾所周知常量1是值型別,值型別是要放在棧上的,而object是引用型別,它需要放在堆上;要把值型別放在堆上就需要執行一次裝箱操作。

這行語句的il**如下,請注意注釋部分說明:

.locals init (

[0] object

objvalue

) //

以上三行il表示宣告object型別的名稱為objvalue的區域性變數

il_0000: nop

il_0001: ldc.i4.s 9//

表示將整型數9放到棧頂

il_0003: box [mscorlib]system.int32 //

執行il box指令,在記憶體堆中申請system.int32型別需要的堆空間

il_0008: stloc.0

//彈出堆疊上的變數,將它儲存到索引為0的區域性變數中

以上就是裝箱所要執行的操作了,執行裝箱操作時不可避免的要在堆上申請記憶體空間,並將堆疊上的值型別資料複製到申請的堆記憶體空間上,這肯定是要消耗記憶體和cpu資源的。我們再看下拆箱操作是怎麼回事:

請看下面的c#**:

object objvalue = 4

;int value = (int)objvalue;

上面的兩行**會執行一次裝箱操作將整形數字常量4裝箱成引用型別object變數objvalue;然後又執行一次拆箱操作,將儲存到堆上的引用變數objvalue儲存到區域性整形值型別變數value中。

同樣我們需要看下il**:

.locals init (

[0] object

objvalue,

[1] int32 '

value')

//上面il宣告兩個區域性變數object型別的objvalue和int32型別的value變數

il_0000: nop

il_0001: ldc.i4.4//

將整型數字4壓入棧

il_0002: box [mscorlib]system.int32 //

執行il box指令,在記憶體堆中申請system.int32型別需要的堆空間

il_0007: stloc.0

//彈出堆疊上的變數,將它儲存到索引為0的區域性變數中

il_0008: ldloc.0

//將索引為0的區域性變數(即objvalue變數)壓入棧

il_0009: unbox.any [mscorlib]system.int32 //

執行il 拆箱指令unbox.any 將引用型別object轉換成system.int32型別

il_000e: stloc.1

//將棧上的資料儲存到索引為1的區域性變數即value

拆箱操作的執行過程和裝箱操作過程正好相反,是將儲存在堆上的引用型別值轉換為值型別並給值型別變數。

裝箱操作和拆箱操作是要額外耗費cpu和記憶體資源的。那如何避免裝箱和拆箱操作呢?有以下方法:

1. 用泛型集合取代arraylist。

2. 用c#自帶的轉換方法,將值型別轉換為引用型別。

下面我們看下使用泛型和不使用泛型引發裝箱拆箱的情況。

1. 使用非泛型集合時引發的裝箱和拆箱操作

看下面的一段**:

var array = new

arraylist();

array.add(1);

array.add(2);

foreach (int value in

array)

」,value);

}

**宣告了乙個arraylist物件,向arraylist中新增兩個數字1,2;然後使用foreach將arraylist中的元素列印到控制台。

在這個過程中會發生兩次裝箱操作和兩次拆箱操作,在向arraylist中新增int型別元素時會發生裝箱,在使用foreach列舉arraylist中的int型別元素時會發生拆箱操作,將object型別轉換成int型別,在執行到console.writeline時,還會執行兩次的裝箱操作;這一段**執行了6次的裝箱和拆箱操作;如果arraylist的元素個數很多,執行裝箱拆箱的操作會更多。

你可以通過使用ilspy之類的工具檢視il**的box,unbox指令檢視裝箱和拆箱的過程

2. 使用泛型集合的情況

請看如下**:

1

var list = new list();

2 list.add(1

);3 list.add(2);4

5foreach (int value in

list)6"

, value);

8 }

**和1中的**的差別在於集合的型別使用了泛型的list,而非arraylist;我們同樣可以通過檢視il**檢視裝箱拆箱的情況,上述**只會在console.writeline()方法時執行2次裝箱操作,不需要拆箱操作。

可以看出泛型可以避免裝箱拆箱帶來的不必要的效能消耗;當然泛型的好處不止於此,泛型還可以增加程式的可讀性,使程式更容易被復用等等。

但是我們注意到,在使用泛型集合的時候,console.writeline()方法時仍然執行2次裝箱操作。能否將這兩次裝箱操作也優化掉呢?這就使用到了第二個方法,用c#自帶的轉換方法,將值型別轉換為引用型別。如下:

var list = new list();

list.add(1);

list.add(2);

foreach (int value in

list)

", value.tostring()));

}

再檢視il**時可以發現,裝箱操作已經被徹底消除了。它實際呼叫的是整形的tostring方法。tostring方法的原型為:

public

override

string

tostring()

它是通過直接操作記憶體來完成從int到string的轉換,效率要比裝箱高很多。所以,在使用其他值型別到字串的轉換並完成拼接時,應當避免使用操作符「+」來完成,而應該使用值型別提供的tostring方法。

第二個方面:避免分配額外的記憶體空間。

對clr來說,string物件是個很特殊的物件,它一旦被賦值就不可改變。在執行時呼叫system.string類中的任何方法或進行任何運算(如「=」賦值,「+」拼接等),都會在記憶體中建立乙個新的字串物件,這也意味著要為該新物件分配新的記憶體空間。像下面的**就會帶來執行時的額外開銷。

private

static

void

test6()

private

static

void

test7()

由於使用string類會在某些場合帶來明顯的效能損耗,所以微軟另外提供了乙個型別stringbuilder來彌補string的不足。

stringbuilder並不會重新建立乙個string物件,它的效率源於預先以非託管的方式分配記憶體。如果stringbuilder沒有先定義長度,則預設分配的長度為16,當stringbuilder字元長度小於等於16時,stringbuilder不會重新分配記憶體。當stringbuilder字元長度大於16時小於32時,stringbuilder又會重新分配記憶體,使之成為16的倍數。在上面的**中,如果預先判斷字串的長度將大於16,則可以為其設定乙個更加合適的長度。

微軟還提供了另外乙個方法來簡化這種操作,即使用string.format方法。string.format方法在內部使用stringbuilder進行字串的格式化。

private

static

void

test9()

private

static

void

test10()

", a, b, c, d));

}

最後總結:如何正確操作字串:

1. 確保盡量少的拆裝箱操作:使用泛型,使用tostring()將值型別轉換為引用型別

2. 避免分配額外的記憶體空間:不用+=, +操作符, 使用stringbuilder, string.format()鏈結多個string

​《編寫高質量**:改善c#程式的157個建議》

C 中如何正確的使用字串String

前言 c 中提供了比較全面的字串處理方法,很多函式都進行了封裝為我們的程式設計工作提供了很大的便利。system.string是最常用的字串操作類,可以幫助開發者完成絕大部分的字串操作功能,使用方便。字串作為所有程式語言中使用最頻繁的一種基礎資料型別。如果使用不慎,將會造成不必要的記憶體開銷,為此而...

建議1 正確操作字串

建議1 正確操作字串 字串應該是所有程式語言中使用最頻繁的一種基礎資料型別。如果使用不慎,我們就會為一次字串的操作所帶來的額外效能開銷而付出代價。本條建議將從兩個方面來 如何規避這類效能開銷 先來介紹第乙個方面,請看下面的兩行 string str1 str1 9 string str2 str2 ...

建議1 正確操作字串

字串是我們常用的基礎資料型別,如果使用不當,養成不好的習慣,會對程式造成額外的開銷。建議 確保盡量少的裝箱 避免分配額外的記憶體空間 確保盡量少的裝箱 1 string str1 str1 9 2 string str2 str2 9.tostring 第一行 需要執行一次裝箱行為int 轉 obj...