# 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 程式碼並解說;
:::