摘要:釋放字符串內存空間的時候,需要先釋放指針所指向的內存空間,再釋放結構體本身的內存空間,效率同樣低下,而且這兩個操作順序不能顛倒。如果存了長度,就不會管你是否有,從頭開始讀字符串,一直讀長度為止即可。有些編譯器不支持數組,可將其改成或均可。
baiyan
全部視頻:https://segmentfault.com/a/11...
源視頻地址:http://replay.xesv5.com/ll/24...
字符串的設計過程在C99的柔性數組標準未發布之前,我們如果想設計一個數據結構,存儲一個字符串,可以很容易地想出如下代碼:
struct string{ ... int len; //存長度(至于為什么存長度下文會講到) char* val; //存真正的字符串值 };
那么我們發現,這樣做有如下缺點:
訪問字符串值的時候,需要先訪問結構體,在訪問指針所指向的內存空間,需要2次內存訪問,效率低下。
釋放字符串內存空間的時候,需要先釋放char *val指針所指向的內存空間,再釋放結構體本身的內存空間,效率同樣低下,而且這兩個操作順序不能顛倒。
那么如何改進呢?很容易想到,我們將字符串值和結構體存儲在一片連續的內存空間就可以了。這樣的話,訪問字符串與釋放字符串的內存空間,均僅需1次內存訪問,在C99柔性數組標準發布之前,改進代碼的方式如下:
int main() { struct string{ int len; }; typedef struct string str; char *s = "he"; str *p = (str*)(malloc(sizeof(str) + strlen(s) + 1)); //分配足夠存下一個字符串的結構體 p->len = strlen(s); memcpy(p + 1, s, strlen(s)); //將字符串拷貝到緊鄰結構體的內存處 }
小插曲:這個代碼的第一版,我還出現了一個指針加法的錯誤,見:關于memcpy一個字符串到緊鄰結構體內存空間處的疑問
我們利用gdb調試一下這段代碼:
首先我們應該給這個結構體分配4 + 2 + 1 = 7字節的內存空間,但是由于內存對齊的原因,最終分配了8字節大小的空間。
結構體本身和len字段的地址均是0x602010,len字段的長度為4B,指針加上4B的len字段長度之后,就應該是字符串he的起始地址,即0x602014,將其強轉為char *,發現正好就是我們存的字符串值"he"。注意不是p+4,而是p+1。因為p+4 = p+4*sizeof(指針p的類型)
由于這樣編寫代碼過于繁瑣,所以C99干脆制定一個標準,使用柔性數組代替上述寫法。其實使用的計算方法和上面一段代碼是一樣的,只不過換了一種簡化的寫法而已,這段代碼最終內存中的存儲情況如下:
PHP7中字符串的實現借助上文講到的字符串數據結構設計思想,PHP中是這樣設計字符串的,它的結構體叫做zend_string:
struct _zend_string { zend_refcounted_h gc; /*引用計數,與垃圾回收相關,暫不展開*/ zend_ulong h; /* 冗余的hash值,計算數組key的哈希值時避免重復計算*/ size_t len; /* 長度 */ char val[1]; /* 柔性數組,真正存放字符串值 */ };
第一個問題:為什么要存長度len?不存長度,直接和C語言一樣通過字符串的"