這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
###C語言中的字串C語言的字串是通過字元數組實現的,每個字串以'\0'
結束。C語言字串的三大操作函數也是常見筆試題。
int strlen(char *s) {char *p = s;while (*p != '\0')p++return p -s;}void strcpy(char *s, char *t) {int i;i = 0;while ((s[i] = t[i]) != '\0')i++;}void strcmp(char *s, char *t) {for ( ; *s == *t; s++, t++)if (*s == '\0')return 0;return *s - *t;}
具體實現的方式為就不說了,主要說說這裡的問題。
- 在計算字串長度的時候,每次都是需要遍曆整個數組,直到
'\0'
為止,時間複雜度是O(n)
,如果字串較長,效能還是會受到影響;
- 在字串複製的時候,有可能會導致越界。如果此時的記憶體結構是:
要給前面的一個字串a
賦值abc
,那麼結果就是後面緊跟著的字串b
也會被修改;
###Golang的字串在Golang1.5之前,底層代碼是用C語言寫的。Golang1.5實現了自舉,這個我還不懂是怎麼具體實現的,就說1.4的。字串不再像C語言那樣用比較簡易的字元數組實現,而是封裝了字串類型string
。
struct String{byte* str;intgo len;}
- 這裡定義了一個結構體,包含str和len欄位。新增的len欄位用於儲存字串長度。這樣計算字串長度的時間複雜度就是
O(1)
,用空間換取了時間效能。
- 字串資料依然是用數組儲存,但是由於有了len欄位儲存長度,所以不再需要
'\0'
來表示結束了。
- 字串的賦值就是指標指向對象的變化,不會有賦值操作,這樣就不會導致越界問題了。
- 如果兩個字串相加,操作方案是重新申請記憶體,儲存相加的兩個字串相加的結果,這樣也不會導致越界。
- 但是還不是很完美,字串是常量,不能通過資料下標的方式訪問和修改。每次修改字串,需要將字串轉換成
[]byte
數組。這樣修改完之後,再轉成字串,這個字串和之前的字串已經不是同一個變數了。
- 字串相加操作也是,任何修改都會導致重新申請記憶體,時間複雜度是
O(m+n)
。Golang字串修改一般是用copy
或者bytes
包的bytes.WriteString
實現。
###Redis的字串Redis是用C語言開發的,Redis支援字串儲存資料。它肯定不會用字元數組來做字串實現。。。它開發了一個叫做簡單動態字串(simpledynamic string,SDS)的類型來實現字串。定義的結構體如下:
struct sdshdr { // 記錄buf數組中已使用位元組的數量 // 等於SDS所儲存字串的長度,用空間解決時間問題int len; // 記錄buf數組中未使用位元組的數量。用於避免緩衝區溢位,如果當前空間不夠,則再分配一個符合大小的空間 int free; // 位元組數組,用於儲存字串。用這個是為了和C語言字串相容char buf[];};
- 和Golang的實現相比,除了長度欄位len和儲存內容的數組buf。計算字串長度的時間複雜度是
O(1)
。
- Redis是用C語言開發的,為了和C語言相容,buf是C語言的字串,以
'\0'
結束。
- 它還增加了free欄位表示空閑空間(別的類似實現可能是用容量欄位,都差不多)。為瞭解決像Golang實現那樣對於寫操作不友好的問題,它會初始化一個較大的記憶體空間,字串的增加會寫入這些提前準備好的空間裡面。一旦用完free的內容,就給buf追加空間。當追加空間的時候,效能會降到和Golang差不多的效能,一般情況還是很好的。
######參考文獻1. How to efficiently concatenate strings in Go? - stackoverflow
原文連結:字串橫向對比:C、Golang、Redis,轉載請註明來源!