在數組和哈希表上工作
在C語言中, 有兩種不同的基礎方法用來在一個結構體中存儲任意數量的獨立數據元素. 兩種方法都有贊成者和反對者.
向量 Vs. 鏈表
應用的編寫通常基於特定類型數據的特性的選擇, 需要存儲多少數據, 以及需要多快速度的檢索. 為了能夠有對等的認知, 我們先來看看簡單的看看這些存儲機制.
向量
向量是一塊連續的內存空間, 它們包含的數據有規律的間隔. 向量最常見的例子就是字符串變量(char *或char []), 它包含了一個接著一個的字符(字節)序列.
char foo[4] = "bar";
這裡, foo[0]包含了字符'b'; 緊接著, 你將在foo[1]中找到字符'a', 最後在foo[3]中是一個空字符'\0'.
將指向其他結構的指針存儲到向量中的用法幾乎是無所不在的, 比如在上一章, 使用zend_get_parameters_array_ex()函數時, 就使用了一個zval的向量. 那裡, 我們看到var_dump()定義了一個zval ***的函數變量, 接著為它分配空間用來存儲zval **指針(最終的數據來自zend_get_parameters_ex()調用)
zval ***args = safe_emalloc(ZEND_NUM_ARGS(), sizeof(zval**), 0);
和訪問字符串中的數組一樣, var_dump()實現中使用args[i]依次傳遞每個zval **元素到內部函數php_var_dump().
向量最大的優點在於運行時單個元素的訪問速度. args[i]這樣的變量引用, 可以很快的計算出它的數據地址(args + i * sizeof(args[0]). 這個索引結構的空間分配和釋放是在單次, 高效的調用中完成的.
鏈表
另外一種常見的存儲數據的方式是鏈表. 對於鏈表而言, 每個數據元素都是一個至少有兩個屬性的結構體: 一個指向鏈表中的下一個節點, 一個則是實際的數據. 考慮下面假設的數據結構:
typedef struct _namelist namelist; struct { struct namelist *next; char *name; } _namelist;
使用這個數據結構的引用需要定義一個變量:
static namelist *people;
鏈表中的第一個名字可以通過檢查people變量的name屬性得到: people->name; 第二個名字則訪問next屬性: people->next->name, 依此類推: people->next->next->name等等, 直到next為NULL表示鏈表中已經沒有其他名字了. 更常見的用法是使用循環迭代鏈表:
void name_show(namelist *p) { while (p) { printf("Name: %s\n", p->name); p = p->next; } }
這種鏈表非常適合於FIFO的鏈式結構, 新的數據被追加到鏈表的末尾, 從另外一端線性的消耗數據:
static namelist *people = NULL, *last_person = NULL; void name_add(namelist *person) { person->next = NULL; if (!last_person) { /* 鏈表中沒有數據 */ people = last_person = person; return; } /* 向鏈表末尾增加新的數據 */ last_person->next = person; /* 更新鏈表尾指針 */ last_person = person; } namelist *name_pop(void) { namelist *first_person = people; if (people) { people = people->next; } return first_person; }