# 2020q3 Homework1 (quiz1) contributed by < [WeiCheng14159](https://github.com/WeiCheng14159) > ###### tags: `sysprog2020` ## Index [TOC] --- ## 指針表示法 * 以下的討論中,指針將如此表示 * 變數值使用==白色==的節點表示 * 指針使用==灰色==節點表示 * 指針的指針將使用==黃色==的節點表示 ```graphviz digraph ptr{ rankdir=LR; node [shape=record,style=filled]; // nodes pp [ label="{<name> node_t ** n_pp| <addr> 0xAA | <val> 0xBB}", fillcolor=yellow ]; p [ label="{<name> node_t * n_p | <addr> 0xBB | <val> 0xCC}", fillcolor=gray ]; n [ label="{<name> node_t n | <addr> 0xCC | <val> ...}", fillcolor=white ]; // edges pp:val -> p:addr; p:val ->n:addr; } ``` * 指針的指針之重點 * __要在函式裡改變==變數值==時,參數必須為指針__ * __要在函式裡改變==指針值==時,參數必須為指針的指針__ ## 函式解析 * 分別使用 Graphviz 和 簡易的記憶體表格解釋指針的操作 ### `add_entry()` ```c=1 void add_entry(node_t **head, int new_value) { node_t **indirect = head; node_t *new_node = malloc(sizeof(node_t)); new_node->value = new_value; new_node->next = NULL; assert(new_node); while (*indirect) indirect = &(*indirect)->next; *indirect = new_node; } ``` * 初始狀態 `indirect` 指向 `head` ```c=1 node_t **indirect = head; ``` ```graphviz digraph g{ rankdir=LR; node [shape=record, style=filled]; subgraph list{ // node node_n1 [fillcolor=gray, label="{<name> n1}"]; node_n2 [fillcolor=gray, label="{<name> n2}"]; node_n3 [fillcolor=gray, label="{<name> n3}"]; node_n4 [fillcolor=gray, label="{<name> n4}"]; node_n5 [fillcolor=gray, label="{<name> n5}"]; head_p [fillcolor=gray, label="{<name> *head}"]; // edge head_p->node_n1->node_n2->node_n3->node_n4->node_n5; } subgraph pp{ // node head_pp [fillcolor=yellow, label="{<name> **head}"]; indirect_pp [fillcolor=yellow, label="{<name> **indirect}"]; // edge indirect_pp->head_p[headport=n]; head_pp->head_p[headport=n]; } } ``` 對應的記憶體操作: | pointer2pointer | | | pointer2value | | | value | | | |:---------------:|---------|------|---------------|---------|------|---------|------------|-----------| | name | address | data | name | address | data | address | data:value | data:next | | head | ... | 200 | n1 | 200 | 100 | 100 | 1 | 101 | | indirect | ... | 200 | n2 | 201 | 101 | 101 | 2 | 102 | | | | | n3 | 202 | 102 | 102 | 3 | 103 | | | | | n4 | 203 | 103 | 103 | 4 | 104 | | | | | n5 | 204 | 104 | 104 | 5 | 0 | * 建立 `new_node` ```c=1 node_t *new_node = malloc(sizeof(node_t)); new_node->value = new_value; new_node->next = NULL; assert(new_node); ``` ```graphviz digraph g{ rankdir=LR; node [shape=record, style=filled]; subgraph list{ // node n1 [fillcolor=gray]; n2 [fillcolor=gray]; n3 [fillcolor=gray]; n4 [fillcolor=gray]; n5 [fillcolor=gray]; head_p [fillcolor=gray, label="{<name> *head}"]; new_node [fillcolor=gray]; // edge head_p->n1->n2->n3->n4->n5; } subgraph pp{ // node head_pp [fillcolor=yellow, label="{<name> **head}"]; indirect_pp [fillcolor=yellow, label="{<name> **indirect}"]; // edge indirect_pp->head_p[headport=n]; head_pp->head_p[headport=n]; } } ``` 對應的記憶體操作: | pointer2pointer | | | pointer2value | | | value | | | |:---------------:|---------|------|---------------|---------|------|---------|------------|-----------| | name | address | data | name | address | data | address | data:value | data:next | | head | ... | 200 | n1 | 200 | 100 | 100 | 1 | 101 | | indirect | ... | 200 | n2 | 201 | 101 | 101 | 2 | 102 | | | | | n3 | 202 | 102 | 102 | 3 | 103 | | | | | n4 | 203 | 103 | 103 | 4 | 104 | | | | | n5 | 204 | 104 | 104 | 5 | 0 | | | | | new_node | 205 | 105 | 105 | new_value | 0 | * 尋找適合之插入位置 ```c=1 while (*indirect) indirect = &(*indirect)->next; *indirect = new_node; ``` ```graphviz digraph g{ rankdir=LR; node [shape=record, style=filled]; subgraph list{ // node n1 [fillcolor=gray]; n2 [fillcolor=gray]; n3 [fillcolor=gray]; n4 [fillcolor=gray]; n5 [fillcolor=gray]; head_p [fillcolor=gray, label="{<name> *head}"]; new_node [fillcolor=gray]; // edge head_p->n1->n2->n3->n4->n5->new_node; } subgraph pp{ // node head_pp [fillcolor=yellow, label="{<name> **head}"]; indirect_pp [fillcolor=yellow, label="{<name> **indirect}"]; // edge indirect_pp->new_node[headport=n]; head_pp->head_p[headport=n]; } } ``` 對應的記憶體內容:因為簡易的記憶體表格過於簡略,無法描述一個 `struct` 中不同成員的位址,故此處省略,僅用圖示表達 ### `remove_entry()` ```c=1 void remove_entry(node_t **head, node_t *entry) { node_t **indirect = head; while ((*indirect) != entry) indirect = &(*indirect)->next; *indirect = entry->next; free(entry); } ``` * 因為這個函式需要改變 `*head` 指針的值,故須要將 `**head` 傳入 ```c=3 node_t **indirect = head; ``` ```graphviz digraph g{ rankdir=LR; node [shape=record, style=filled]; subgraph list{ // node node_n1 [fillcolor=gray, label="{<name> n1}"]; node_n2 [fillcolor=gray, label="{<name> n2}"]; node_n3 [fillcolor=gray, label="{<name> n3}"]; node_n4 [fillcolor=gray, label="{<name> n4}"]; node_n5 [fillcolor=gray, label="{<name> n5}"]; head_p [fillcolor=gray, label="{<name> *head}"]; entry_p [fillcolor=gray, label="{<name> *entry}"]; // edge head_p->node_n1->node_n2->node_n3->node_n4->node_n5; entry_p->node_n3[headport=n]; } subgraph pp{ // node head_pp [fillcolor=yellow, label="{<name> **head}"]; indirect_pp [fillcolor=yellow, label="{<name> **indirect}"]; // edge indirect_pp->head_p[headport=n]; head_pp->head_p[headport=n]; } } ``` 對應的記憶體操作: | pointer2pointer | | | pointer2value | | | value | | | |:---------------:|---------|------|---------------|---------|------|---------|------------|-----------| | name | address | data | name | address | data | address | data:value | data:next | | head | ... | 200 | n1 | 200 | 100 | 100 | 1 | 101 | | indirect | ... | 200 | n2 | 201 | 101 | 101 | 2 | 102 | | | | | n3 | 202 | 102 | 102 | 3 | 103 | | | | | n4 | 203 | 103 | 103 | 4 | 104 | | | | | n5 | 204 | 104 | 104 | 5 | 0 | | | | | entry | 205 | 102 | | | | * 當程式離開 `while loop` 之後的情況 ```c=5 while ((*indirect) != entry) indirect = &(*indirect)->next; ``` ```graphviz digraph initial{ rankdir=LR; node [shape=record, style=filled]; subgraph linked_list{ node_n1 [fillcolor=gray, label="{<name> n1}"]; node_n2 [fillcolor=gray, label="{<name> n2}"]; node_n3 [fillcolor=gray, label="{<name> n3}"]; node_n4 [fillcolor=gray, label="{<name> n4}"]; node_n5 [fillcolor=gray, label="{<name> n5}"]; head_p [fillcolor=gray, label="{<name> *head}"]; entry_p [fillcolor=gray, label="{<name> *entry}"]; head_p->node_n1->node_n2->node_n3->node_n4->node_n5; entry_p->node_n3[headport=n]; } subgraph pp{ head_pp [fillcolor=yellow, label="{<name> **head}"]; indirect_pp [fillcolor=yellow, label="{<name> **indirect}"]; indirect_pp->node_n2[headport=n]; head_pp->head_p[headport=n]; } } ``` 對應的記憶體操作: | pointer2pointer | | | pointer2value | | | value | | | |:---------------:|---------|---------------|---------------|---------|------|---------|------------|-----------| | name | address | data | name | address | data | address | data:value | data:next | | head | ... | 200 | n1 | 200 | 100 | 100 | 1 | 101 | | indirect | ... | 200=>201=>202 | n2 | 201 | 101 | 101 | 2 | 102 | | | | | n3 | 202 | 102 | 102 | 3 | 103 | | | | | n4 | 203 | 103 | 103 | 4 | 104 | | | | | n5 | 204 | 104 | 104 | 5 | 0 | | | | | entry | 205 | 102 | | | | * `*indirect` 指向 `entry->next` ```c=8 *indirect = entry->next; free(entry); ``` ```graphviz digraph initial{ rankdir=LR; node [shape=record, style=filled]; subgraph linked_list{ node_n1 [fillcolor=gray, label="{<name> n1}"]; node_n2 [fillcolor=gray, label="{<name> n2}"]; node_n3 [color=red, label="{<name> n3}"]; node_n4 [fillcolor=gray, label="{<name> n4}"]; node_n5 [fillcolor=gray, label="{<name> n5}"]; head_p [fillcolor=gray, label="{<name> *head}"]; entry_p [fillcolor=gray, label="{<name> *entry}"]; head_p->node_n1->node_n2 node_n3 node_n4->node_n5; entry_p->node_n3[headport=n, tailport=e]; node_n2->node_n4[color=red]; } subgraph pp{ head_pp [fillcolor=yellow, label="{<name> **head}"]; indirect_pp [fillcolor=yellow, label="{<name> **indirect}"]; indirect_pp->node_n2[headport=n, tailport=s]; head_pp->head_p[headport=n, tailport=s]; } } ``` 對應的記憶體操作: | pointer2pointer | | | pointer2value | | | value | | | |:---------------:|---------|---------------|---------------|---------|----------|---------|------------|-----------| | name | address | data | name | address | data | address | data:value | data:next | | head | ... | 200 | n1 | 200 | 100 | 100 | 1 | 101 | | indirect | ... | 200=>201=>202 | n2 | 201 | 101 | 101 | 2 | 102 | | | | | n3 | 202 | 102=>103 | 102 | 3=>0 | 103=>0 | | | | | n4 | 203 | 103 | 103 | 4 | 104 | | | | | n5 | 204 | 104 | 104 | 5 | 0 | | | | | entry | 205 | 102 | | | | ### `swap_pair()` ```c=1 node_t *swap_pair(node_t *head) { for (node_t **node = &head; *node && (*node)->next; node = &(*node)->next->next) { node_t *tmp = *node; *node = (*node)->next; tmp->next = (*node)->next; (*node)->next = tmp; } return head; } ``` * 初始狀況 ```c=3 for (node_t **node = &head; *node && (*node)->next; node = &(*node)->next->next) { node_t *tmp = *node; ``` ```graphviz digraph initial{ rankdir=LR; node [shape=record, style=filled]; subgraph linked_list{ n1 [fillcolor=gray, label="{<name> n1}"]; n2 [fillcolor=gray, label="{<name> n2}"]; n3 [fillcolor=gray, label="{<name> n3}"]; n4 [fillcolor=gray, label="{<name> n4}"]; n5 [fillcolor=gray, label="{<name> n5}"]; n1->n2->n3->n4->n5; } subgraph pp{ node_pp [fillcolor=yellow, label="{<name> **node}"]; node_pp->head_p[headport=n]; head_p [fillcolor=gray, label="{<name> *head}"]; head_p->n1; tmp_p [fillcolor=gray, label="{<name> *tmp}"]; tmp_p->n1; } } ``` 對應的記憶體操作: | pointer2pointer | | | pointer2value | | | value | | | |:---------------:|---------|------|---------------|---------|------|---------|------------|-----------| | name | address | data | name | address | data | address | data:value | data:next | | node | ... | 205 | n1 | 200 | 100 | 100 | 1 | 101 | | | | | n2 | 201 | 101 | 101 | 2 | 102 | | | | | n3 | 202 | 102 | 102 | 3 | 103 | | | | | n4 | 203 | 103 | 103 | 4 | 104 | | | | | n5 | 204 | 104 | 104 | 5 | 0 | | | | | head | 205 | 100 | | | | | | | | tmp | 206 | 100 | | | | * 第一個 `iteration` ```c=5 *node = (*node)->next; tmp->next = (*node)->next; (*node)->next = tmp; ``` ```graphviz digraph iter1{ rankdir=LR; node [shape=record, style=filled]; subgraph linked_list{ n1 [fillcolor=gray, label="{<name> n1}"]; n2 [fillcolor=gray, label="{<name> n2}"]; n3 [fillcolor=gray, label="{<name> n3}"]; n4 [fillcolor=gray, label="{<name> n4}"]; n5 [fillcolor=gray, label="{<name> n5}"]; n1 n2 n3->n4->n5; n1->n3[color=red]; n1->n2[color=gray]; n2->n3[color=gray]; n2->n1[color=red, headport=ne, tailport=e]; } subgraph pp{ node_pp [fillcolor=yellow, label="{<name> **node}"]; node_pp->head_p[headport=n]; head_p [fillcolor=gray, label="{<name> *head}"]; head_p->n2; tmp_p [fillcolor=gray, label="{<name> *tmp}"]; tmp_p->n1; } } ``` 對應的記憶體操作: | pointer2pointer | | | pointer2value | | | value | | | |:---------------:|---------|------|---------------|---------|----------|---------|------------|-----------| | name | address | data | name | address | data | address | data:value | data:next | | node | ... | 205 | n1 | 200 | 100 | 100 | 1 | 101=>102 | | | | | n2 | 201 | 101 | 101 | 2 | 102=>100 | | | | | n3 | 202 | 102 | 102 | 3 | 103 | | | | | n4 | 203 | 103 | 103 | 4 | 104 | | | | | n5 | 204 | 104 | 104 | 5 | 0 | | | | | head | 205 | 100=>101 | | | | | | | | tmp | 206 | 100 | | | | * 下一個初始狀況 ```c=3 for (node_t **node = &head; *node && (*node)->next; node = &(*node)->next->next) { node_t *tmp = *node; ``` ```graphviz digraph g{ rankdir=LR; node [shape=record, style=filled]; subgraph linked_list{ n1 [fillcolor=gray, label="{<name> n1}"]; n2 [fillcolor=gray, label="{<name> n2}"]; n3 [fillcolor=gray, label="{<name> n3}"]; n4 [fillcolor=gray, label="{<name> n4}"]; n5 [fillcolor=gray, label="{<name> n5}"]; n1->n3->n4->n5; n2->n1; } subgraph pp{ node_pp [fillcolor=yellow, label="{<name> **node}"]; node_pp->n3[headport=n]; head_p [fillcolor=gray, label="{<name> *head}"]; head_p->n2; tmp_p [fillcolor=gray, label="{<name> *tmp}"]; tmp_p->n3[headport=s]; } } ``` 對應的記憶體操作: | pointer2pointer | | | pointer2value | | | value | | | |:---------------:|---------|----------|---------------|---------|----------|---------|------------|-----------| | name | address | data | name | address | data | address | data:value | data:next | | node | ... | 205=>202 | n1 | 200 | 100 | 100 | 1 | 101=>102 | | | | | n2 | 201 | 101 | 101 | 2 | 102=>100 | | | | | n3 | 202 | 102 | 102 | 3 | 103 | | | | | n4 | 203 | 103 | 103 | 4 | 104 | | | | | n5 | 204 | 104 | 104 | 5 | 0 | | | | | head | 205 | 100=>101 | | | | | | | | tmp | 206 | 100=>102 | | | | ### `reverse()` ```c=1 node_t *reverse(node_t *head) { node_t *cursor = NULL; while (head) { node_t *next = head->next; head->next = cursor; cursor = head; head = next; } return cursor; } ``` * 進入迴圈之前 ```graphviz digraph g{ rankdir=LR; node [shape=record, style=filled]; subgraph linked_list{ n1 [fillcolor=gray, label="{<name> n1}"]; n2 [fillcolor=gray, label="{<name> n2}"]; n3 [fillcolor=gray, label="{<name> n3}"]; n4 [fillcolor=gray, label="{<name> n4}"]; n5 [fillcolor=gray, label="{<name> n5}"]; n1->n2->n3->n4->n5; cursor [fillcolor=gray]; } subgraph pp{ head_p [fillcolor=gray, label="{<name> *head}"]; head_p->n1; } } ``` * 第一輪迴圈執行結束 ```c=1 while (head) { node_t *next = head->next; head->next = cursor; cursor = head; head = next; } ``` ```graphviz digraph g{ rankdir=LR; node [shape=record, style=filled]; subgraph linked_list{ n1 [fillcolor=gray, label="{<name> n1}"]; n2 [fillcolor=gray, label="{<name> n2}"]; n3 [fillcolor=gray, label="{<name> n3}"]; n4 [fillcolor=gray, label="{<name> n4}"]; n5 [fillcolor=gray, label="{<name> n5}"]; n2->n3->n4->n5; cursor [fillcolor=gray]; next [fillcolor=gray]; null[fillcolor=gray]; next->n2; n1->n2 [color=gray]; n1->null[color=red]; } subgraph pp{ head_p [fillcolor=gray, label="{<name> *head}"]; head_p->n2; cursor->n1; } } ``` * 第二輪迴圈執行結束 ```graphviz digraph h{ rankdir=LR; node [shape=record, style=filled]; subgraph linked_list{ n1 [fillcolor=gray, label="{<name> n1}"]; n2 [fillcolor=gray, label="{<name> n2}"]; n3 [fillcolor=gray, label="{<name> n3}"]; n4 [fillcolor=gray, label="{<name> n4}"]; n5 [fillcolor=gray, label="{<name> n5}"]; n3->n4->n5; cursor [fillcolor=gray]; next [fillcolor=gray]; null[fillcolor=gray]; next->n3; n2->n1[color=red]; n1->null; n2->n3[color=gray]; } subgraph pp{ head_p [fillcolor=gray, label="{<name> *head}"]; head_p->n3; cursor->n2; } } ``` * 第三輪迴圈執行結束 ```graphviz digraph h{ rankdir=LR; node [shape=record, style=filled]; subgraph linked_list{ n1 [fillcolor=gray, label="{<name> n1}"]; n2 [fillcolor=gray, label="{<name> n2}"]; n3 [fillcolor=gray, label="{<name> n3}"]; n4 [fillcolor=gray, label="{<name> n4}"]; n5 [fillcolor=gray, label="{<name> n5}"]; n4->n5; cursor [fillcolor=gray]; next [fillcolor=gray]; null[fillcolor=gray]; next->n4; n3->n2[color=red]; n2->n1->null; n3->n4[color=gray]; } subgraph pp{ head_p [fillcolor=gray, label="{<name> *head}"]; head_p->n4; cursor->n3; } } ``` * 以此類推直到執行結束 ```graphviz digraph h{ rankdir=LR; node [shape=record, style=filled]; subgraph linked_list{ n1 [fillcolor=gray, label="{<name> n1}"]; n2 [fillcolor=gray, label="{<name> n2}"]; n3 [fillcolor=gray, label="{<name> n3}"]; n4 [fillcolor=gray, label="{<name> n4}"]; n5 [fillcolor=gray, label="{<name> n5}"]; cursor [fillcolor=gray]; } subgraph pp{ cursor->n5->n4->n3->n2->n1; } } ``` ## 改寫 swap_pair 和 reverse * 運用指標的指標改寫 `swap_pair` 和 `reverse` ### swap_pair 改寫 * swap_pair 原來就運用到指標的指標,稍微改一下就行 ```c=1 void swap_pair(node_t **head) { for (node_t **node = head; *node && (*node)->next; node = &(*node)->next->next) { node_t *tmp = *node; *node = (*node)->next; tmp->next = (*node)->next; (*node)->next = tmp; } } ``` ### reverse 改寫 * reverse 重點是最後一行的 `*head = cursor;` 運用指標的指標更改 `*head` 指標的值,觀念來自老師的[你所不知道的 C 語言:指標篇 (上) (2018-02-05)](https://youtu.be/G7vERppua9o?list=PL6S9AqLQkFpqAHXlqoH2JpvOSmku7WjRU&t=6934) ```c=1 void reverse(node_t **head) { node_t *cursor = NULL; while (*head) { node_t *next = (*head)->next; (*head)->next = cursor; cursor = *head; *head = next; } *head = cursor; } ``` ## Fisher–Yates shuffle * TBD