# 2019q1 第 3 週測驗題 (下) :::info 目的: 檢驗學員對 C 程式設計的認知 ::: --- ### 測驗 `1` 以下程式碼是個 [XOR linked list](https://en.wikipedia.org/wiki/XOR_linked_list) 的實作,在這樣的資料結構可表示為: ``` 1 2 3 4 data HAT CAT EAT BAT link 2 2 6 3 ``` 要往下一格移動時,我們需要前一格在哪和這一格的位置。例如我們現在在 `HAT` (1), 已知前一格是 (0),那麼下一格就是 `link(1) XOR 0` = `2 XOR 0` = 2,也就是 `CAT`。繼續,現在位於 `CAT` (2),前一格在 (1),於是下一格就是 `link(2) XOR 1` = `2 XOR 1` = 3,亦即 `EAT`,依此類推。只要當找出來的下一格是 (0) 就結束。 ```clike #include <stdio.h> #include <stdlib.h> typedef struct { unsigned long lxr; int data1, data2; } item; item *lh = NULL; typedef struct { item *left, *right; } lxr_info; void print_at(lxr_info *p_lxri, item *pli) { if (pli) printf("data1 = %d, data2 = %d\n", pli->data1, pli->data2); } static inline item *extract_address(item *lxr, item *other) { return (item *) (lxr->lxr ^ (unsigned long) other); } static inline void shift_lxr(item *lxr, item *known, item *update) { lxr->lxr = (lxr->lxr ^ (unsigned long) known) ^ (unsigned long) update; } /* Walk the list starting at the header */ void list_walk(void) { item *pcur = lh; lxr_info lxri = {.left = NULL, .right = NULL}; lxri.right = extract_address(pcur, lxri.left); while (pcur) { print_at(&lxri, pcur); pcur = lxr_move_right(&lxri, pcur); } } /* Append an item where we are */ item *append_item(lxr_info *p_lxri, item *p_current, int d1, int d2) { item *p_this, *p_left, *p_right; if ((p_this = calloc(1, sizeof(item)))) { p_left = p_lxri->left = p_current; p_right = p_lxri->right; UCCU; VCCV; p_this->lxr = (unsigned long) p_current ^ (unsigned long) p_lxri->right; p_this->data1 = d1; p_this->data2 = d2; } return p_this; } int main(int argc, char **argv) { int index = 0; int maxrun = 20; item *pcur = lh, *pnli; lxr_info lxri = {.left = NULL, .right = NULL}; while (index < maxrun) { if ((pnli = append_item(&lxri, pcur, 3 * index, 3 * index + 1)) == NULL) { exit(2); } pcur = pnli; if (!lh) lh = pcur; index++; } list_walk(); return 0; } ``` 請補完程式碼。 ==作答區== UCCU = ? * `(a)` shift_lxr(p_left, p_right, p_this); * `(b)` shift_lxr(p_right, p_left, p_this); * `(c)` if (p_left) shift_lxr(p_left, p_right, p_this); * `(d)` shift_lxr(p_this ? p_left: p_right, p_this, p_this) * `(e)` shift_lxr(p_this ? p_right: p_left, p_this, p_this) VCCV = ? * `(a)` shift_lxr(p_left, p_right, p_this); * `(b)` shift_lxr(p_right, p_left, p_this); * `(c)` if (p_right) (p_right, p_left, p_this); * `(d)` shift_lxr(p_this ? p_left: p_right, p_this, p_this) * `(e)` shift_lxr(p_this ? p_right: p_left, p_this, p_this) :::success 延伸問題: 1. 設計實驗探討 XOR linked list 的記憶體開銷,相較於典型的設計,能省多少呢? 2. 在 Linux 核心原始程式碼找出特製的 linked list,並予以解說 ::: --- ### 測驗 `1` 考慮以下實作 [Reference counting](https://en.wikipedia.org/wiki/Reference_counting) 的程式碼: ```clike #include <stddef.h> #include <stdio.h> #include <stdlib.h> struct ref { void (*free)(const struct ref *); int count; }; static inline void ref_inc(const struct ref *ref) { ((struct ref *) ref)->count++; } static inline void ref_dec(const struct ref *ref) { if (--((struct ref *) ref)->count == 0) ref->free(ref); } #define container_of(ptr, type, member) \ ((type *) ((char *) (ptr) -offsetof(type, member))) struct node { char id[64]; float value; struct node *next; struct ref refcount; }; static void node_free(const struct ref *ref) { struct node *node = container_of(ref, struct node, refcount); struct node *child = node->next; free(node); ABA; } struct node *node_create(char *id, float value) { struct node *node = malloc(sizeof(*node)); snprintf(node->id, sizeof(node->id), "%s", id); node->value = value; node->next = NULL; node->refcount = (struct ref){node_free, 1}; return node; } void node_push(struct node **nodes, char *id, float value) { struct node *node = node_create(id, value); node->next = *nodes; *nodes = node; } struct node *node_pop(struct node **nodes) { struct node *node = *nodes; *nodes = (*nodes)->next; if (*nodes) CDC; return node; } void node_dump(struct node *node) { for (; node; node = node->next) printf("%s = %f\n", node->id, node->value); } int main(void) { struct node *nodes = NULL; char id[64]; float value; node_push(&nodes, "foo", 0.1f); node_dump(nodes); struct node *old = node_pop(&nodes); node_push(&nodes, "bar", 0.2f); node_dump(nodes); ref_dec(&old->refcount); ref_dec(&nodes->refcount); return 0; } ``` 請補完程式碼。 ==作答區== ABA = ? * `(a)` ref_dec(child->ref_dec) * `(b)` if (child) ref_dec(child->ref_dec) * `(c)` if (child) ref_dec(*child->ref_dec) * `(d)` if (child) ref_dec(&child->refcount); CDC = ? * `(a)` ref_inc(*nodes->refcount) * `(b)` ref_inc((*node)->refcount) * `(c)` ref_inc(&*nodes->refcount) * `(d)` ref_inc((*nodes).refcount) * `(e)` ref_inc(&(*nodes)->refcount) * `(f)` ref_inc(*nodes) :::success 延伸問題: 1. 這個 reference counting 實作沒考慮到 [thread safety](https://en.wikipedia.org/wiki/Thread_safety),請著手改善並設計驗證機制; 2. 在 Linux 核心找出類似的 reference counting 程式碼並解說; :::