在數組和雜湊表上工作
在C語言中, 有兩種不同的基礎方法用來在一個結構體中儲存任意數量的獨立資料元素. 兩種方法都有贊成者和反對者.
向量 Vs. 鏈表
應用的編寫通常基於特定類型資料的特性的選擇, 需要儲存多少資料, 以及需要多快速度的檢索. 為了能夠有對等的認知, 我們先來看看簡單的看看這些儲存機制.
向量
向量是一塊連續的記憶體空間, 它們包含的資料有規律的間隔. 向量最常見的例子就是字串變數(char *或char []), 它包含了一個接著一個的字元(位元組)序列.
char foo[4] = "bar";
這裡, foo[0]包含了字元'b'; 緊接著, 你將在foo[1]中找到字元'a', 最後在foo[3]中是一個Null 字元'\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; }