1、前言
在erlang資料型別的c原始碼決議(1)-Eterm講到,當Eterm的低2位為01B時,就是List型別,剩余的位數表示一個指向串列的指標,
另外就是,先講結論,有助于理解,Erlang中的List在C的層面上是一個鏈表,
2、宏定義
// CONS用于構建一個鏈表節點
#define CONS(hp, car, cdr) \
(CAR(hp)=(car), CDR(hp)=(cdr), make_list(hp))
// CAD:取出鏈表結點的資料部分
#define CAR(x) ((x)[0])
// CDR:取出鏈表結點的Next指標
#define CDR(x) ((x)[1])
// make_list最后會呼叫到_unchecked_make_list(x), 向Eterm打上list的標簽,實質上是置Eterm的低2位為10B
// COMPRESS_POINTER為壓縮,與下面的EXPAND_POINTER對應
#define make_list(x) _ET_APPLY(make_list,(x))
#define _unchecked_make_list(x) ((Uint) COMPRESS_POINTER(x) + TAG_PRIMARY_LIST)
// list_val最后會呼叫到_unchecked_list_val(x), 將低2位的標簽去掉,再取對應地址的值
#define list_val(x) _ET_APPLY(list_val,(x))
#define _unchecked_list_val(x) ((Eterm*) EXPAND_POINTER((x) - TAG_PRIMARY_LIST))
(這些蛋疼的命名來源于lisp語言,有興趣可以查一下)
3. 幾個常用函式的決議
1. erlang:length/1
BIF_RETTYPE length_1(BIF_ALIST_1)
{
Eterm list;
Uint i;
if (is_nil(BIF_ARG_1))
BIF_RET(SMALL_ZERO);
if (is_not_list(BIF_ARG_1)) {
BIF_ERROR(BIF_P, BADARG);
}
list = BIF_ARG_1;
// i為回傳值,代表鏈表長度
i = 0;
// 回圈取next,next不為空的話i自增,直到nil
while (is_list(list)) {
i++;
// CDR相當于取出結點Next指標
list = CDR(list_val(list));
}
if (is_not_nil(list)) {
BIF_ERROR(BIF_P, BADARG);
}
BIF_RET(make_small(i));
}
從這個函式和上述CONS的定義,都可以得出List在C層面上是一個鏈表,
2. erlang:tuple_to_list/1
BIF_RETTYPE tuple_to_list_1(BIF_ALIST_1)
{
Uint n;
Eterm *tupleptr;
Eterm list = NIL;
Eterm* hp;
if (is_not_tuple(BIF_ARG_1)) {
BIF_ERROR(BIF_P, BADARG);
}
// 這里相當于取得tuple的指標
tupleptr = tuple_val(BIF_ARG_1);
// n為tuple的長度
n = arityval(*tupleptr);
// 將tuple看成一個陣列,tupleptr[n]就是c語言層面的陣列
// 在行程堆上分配空間,由于一個鏈表節點要存盤資料和Next指標,所以申請的空間大小為tuple長度的2倍
hp = HAlloc(BIF_P, 2 * n);
tupleptr++;
// 從陣列的最后一個元素開始回圈
while(n--) {
// 生成鏈表結點
list = CONS(hp, tupleptr[n], list);
// 鏈表結點占用雙倍的空間,所以+=2
hp += 2;
}
BIF_RET(list);
}
4、代碼來源
github上OTP17代碼的erl_term.h和erl_bif_lists.c
地址:https://github.com/erlang/otp/tree/maint-17
下一篇:erlang資料型別的c原始碼決議(3)-Tuple
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/214743.html
標籤:其他
