# 2018q3 Homework2 (lab0)
contributed by < `danielian1121` >
>* 請同步更新原專案最新的 commit
>* commit message
> * [Singly linked list](https://github.com/danielian1121/lab0-c/commit/c42bb96d390ebdb4dd6832a3817d356a892144f7) 請遵循 commit message 的規範撰寫
> * [Finish, add comments and make code cleaner](https://github.com/danielian1121/lab0-c/commit/e1d6c4039f4ec5594a9ce891bd0ebe0cfd608324) 不需要寫出 Finish, 與您的修改無關
> * [Change strdup to malloc](https://github.com/danielian1121/lab0-c/commit/121ac7488be36c5276b36bbbd32c7dd6c4e71146) 增加說明為什麼要抽換為 malloc,且此有一個 Makefile 的修改與此 commit 描述無關
>
>[name=課程助教][color=red]
>
## 實驗環境
```bash
sukamo:~$ cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.1 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.1 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
```
## struct 架構
```clike
typedef struct ELE {
char *value;
struct ELE *next;
struct ELE *prev;
} list_ele_t;
typedef struct {
list_ele_t *head;
list_ele_t *tail;
unsigned int count;
} queue_t;
```
:::info
`You may add other fields to the structure list_ele, although you need not do so.`
題目貌似沒有規定不能用 doubly linked list,但看到其他同學的共筆幾乎都沒有改到 `list_ele_t` 的架構,之後會開一個 branch 來實做 singly linked list
:::
## funtion 實做
#### q_new
```clike
queue_t *q_new()
{
queue_t *q = malloc(sizeof(queue_t));
if (q == NULL) {
return NULL;
}
q->head = NULL;
q->tail = NULL;
q->count = 0;
return q;
}
```
#### q_free
```clike
void q_free(queue_t *q)
{
if (q == NULL)
return;
list_ele_t *temp = q->head;
list_ele_t *readyFree;
while (temp) {
readyFree = temp;
temp = temp->next;
//free(readyFree->value);
free(readyFree);
}
free(q);
}
```
* 合理來說,註解掉的那一行應該要存在,否則每個 element 的 value 會沒有被 free 掉,但是加上去後 make test 又會跑不過,真的是很奇怪......
* 有看到同學的共筆也有提起這件事情([來源](https://hackmd.io/s/HkWLADMK7))
* harness.h 有下列這段程式
```clike
/* Tested program use our versions of malloc and free */
#define malloc test_malloc
#define free test_free
```
* 因此我們程式中所用的 malloc and free 是使用測試程式中的某個函式,而我在 copy value 的時候使用了 strdup,此函式會在裡面呼叫 malloc ,因而繞過測試程式,因此在準備 free 的時候,也會誤判為此區段沒有事先 malloc 而發生錯誤
:::info
目前已改成用 malloc 搭配 strcpy 的方式來 copy value
:::
#### q_insert_head
```clike
bool q_insert_head(queue_t *q, char *s)
{
/** If q is NULL, return false. */
if (q == NULL)
return false;
list_ele_t *newh;
newh = malloc(sizeof(list_ele_t));
/** If malloc failed, return false. */
if (newh == NULL)
return false;
char *s_copy = malloc(strlen(s) + 1);
/** If malloc failed, return false. */
if (s_copy == NULL) {
free(newh);
return false;
}
strcpy(s_copy, s);
/** Set the new element. */
newh->value = s_copy;
newh->next = q->head;
newh->prev = NULL;
q->head = newh;
/** If the new element is the first, set q->tail as q->head. */
if (q->head->next == NULL)
q->tail = q->head;
else
q->head->next->prev = q->head;
/** Add count. */
q->count++;
return true;
}
```
#### q_insert_tail
* 作業有要求,此函式的時間複雜度必須為 $O(1)$ ,因此 queue_t 中才有一個指向 tail 的 pointer 存在
* 與 insert_head 大同小異,只是指標指向的位置要特別注意
```clike
bool q_insert_tail(queue_t *q, char *s)
{
/** If q is NULL, return false. */
if (q == NULL)
return false;
list_ele_t *newh;
newh = malloc(sizeof(list_ele_t));
/** If malloc failed, return false. */
if (newh == NULL)
return false;
char *s_copy = malloc(strlen(s) + 1);
/** If malloc failed, return false. */
if (s_copy == NULL) {
free(newh);
return false;
}
strcpy(s_copy, s);
/** Set the new element. */
newh->value = s_copy;
newh->prev = q->tail;
newh->next = NULL;
q->tail = newh;
/** If the new element is the first, set q->head as q->tail. */
if (q->tail->prev == NULL)
q->head = q->tail;
else
q->tail->prev->next = q->tail;
/** Add count. */
q->count++;
return true;
}
```
#### q_remove_head
* 在刪除之際,也必須把即將刪除的 element 的 value 保存 bufsize 大小存到 sp 中
* 無法直接用 `strcpy(q->head->value, sp)` ,因為有可能會造成 Buffer Overflow
* 因此必須先知道 q->head->value 的長度,再依照結果來作不同的事情
```clike
bool q_remove_head(queue_t *q, char *sp, size_t bufsize)
{
/** If q is NULL or q->head is NULL, return false. */
if (q == NULL || q->head == NULL)
return false;
/** If len > bufsize, use strncpy to copy.
* Otherwise, use strcpy.
*/
if (sp != NULL) {
size_t len = strlen(q->head->value);
if (len > bufsize - 1) {
strncpy(sp, q->head->value, bufsize - 1);
sp[bufsize - 1] = '\0';
} else {
strcpy(sp, q->head->value);
}
}
/** Check the element is the last or not. */
if (q->head == q->tail)
q->tail = NULL;
/** Delete the head element. */
list_ele_t *temp;
temp = q->head;
q->head = q->head->next;
if (q->head != NULL)
q->head->prev = NULL;
/** Free the head element. */
free(temp->value);
free(temp);
/** Subtract count. */
q->count--;
return true;
}
```
#### q_size
* 題目要求時間複雜度必須為 $O(1)$,因此也在 queue_t 中加入變數 count 來計算 size
```clike
int q_size(queue_t *q)
{
return q ? q->count : 0;
}
```
#### q_reverse
* 在 doubly linked list 中,此函式實做比較簡單,只須交換 queue 中的 next and prev ,再交換 queue_t 的 head and tail 即可
```clike
void q_reverse(queue_t *q)
{
if (q == NULL || q->head == NULL)
return;
list_ele_t *temp = q->head;
list_ele_t *swap;
/** Swap next and prev in every element from head to tail. */
while (temp) {
swap = temp->next;
temp->next = temp->prev;
temp->prev = swap;
temp = swap;
}
/* swap head and tail */
swap = q->head;
q->head = q->tail;
q->tail = swap;
}
```
## Singly linked list
:::info
已有新 branch "singly"
:::
#### q_reverse
```graphviz
digraph G {
rankdir=LR;
node [shape=record];
subgraph cluster_4 {
a4[label=A];
b4[label=B];
c4[label=C];
d4[label=D];
head4->d4;a4->NULL4;
d4->c4;
b4->a4;c4->b4;
}
prev4[label=prev,shape=plaintext];
now4[label=now,shape=plaintext];
next4[label=next,shape=plaintext];
head4[label=head,shape=plaintext];
NULL4[label=NULL,shape=plaintext];
prev4->b4;
now4->c4;
next4->d4;
subgraph cluster_3 {
a3[label=A];
b3[label=B];
c3[label=C];
d3[label=D];
head3->a3->b3;d3->NULL3;
b3->a3;c3->b3;
}
prev3[label=prev,shape=plaintext];
now3[label=now,shape=plaintext];
next3[label=next,shape=plaintext];
head3[label=head,shape=plaintext];
NULL3[label=NULL,shape=plaintext];
prev3->b3;
now3->c3;
next3->d3;
subgraph cluster_2 {
a2[label=A];
b2[label=B];
c2[label=C];
d2[label=D];
head2->a2->b2;c2->d2->NULL2;
b2->a2;
}
prev2[label=prev,shape=plaintext];
now2[label=now,shape=plaintext];
next2[label=next,shape=plaintext];
head2[label=head,shape=plaintext];
NULL2[label=NULL,shape=plaintext];
prev2->b2;
now2->c2;
next2->d2;
subgraph cluster_1 {
a1[label=A];
b1[label=B];
c1[label=C];
d1[label=D];
head1->a1->b1;c1->d1->NULL1;
b1->a1;
}
prev1[label=prev,shape=plaintext];
now1[label=now,shape=plaintext];
next1[label=next,shape=plaintext];
head1[label=head,shape=plaintext];
NULL1[label=NULL,shape=plaintext];
prev1->a1;
now1->b1;
next1->c1;
subgraph cluster_0 {
a0[label=A];
b0[label=B];
c0[label=C];
d0[label=D];
head0->a0->b0->c0->d0->NULL0;
}
prev0[label=prev,shape=plaintext];
now0[label=now,shape=plaintext];
next0[label=next,shape=plaintext];
head0[label=head,shape=plaintext];
NULL0[label=NULL,shape=plaintext];
prev0->a0;
now0->b0;
next0->c0;
}
```