Try   HackMD

2025q1 Homework1 (lab0)

contributed by < TING0419 >

作業書寫規範:

  • 無論標題和內文中,中文和英文字元之間要有空白字元 (對排版和文字搜尋有利)
  • 文字訊息 (尤其是程式執行結果) 請避免用圖片來表示,否則不好搜尋和分類
  • 共筆書寫請考慮到日後協作,避免過多的個人色彩,用詞儘量中性
  • 不要在筆記內加入 [TOC] : 筆記左上方已有 Table of Contents (TOC) 功能,不需要畫蛇添足
  • 不要變更預設的 CSS 也不要加入任何佈景主題: 這是「開發紀錄」,用於評分和接受同儕的檢閱
  • 在筆記中貼入程式碼時,避免非必要的行號,也就是該手動將 c=cpp= 變更為 ccpp。行號只在後續討論明確需要行號時,才要出現,否則維持精簡的展現。可留意「你所不知道的 C 語言: linked list 和非連續記憶體」裡頭程式碼展現的方式
  • HackMD 不是讓你張貼完整程式碼的地方,GitHub 才是!因此你在開發紀錄只該列出關鍵程式碼 (善用 diff 標示),可附上對應 GitHub commit 的超連結,列出程式碼是為了「檢討」和「便於他人參與討論」
  • 留意科技詞彙的使用,請參見「資訊科技詞彙翻譯」及「詞彙對照表
  • 不要濫用 :::info, :::success, :::warning 等標示,儘量用清晰的文字書寫。:::danger 則僅限授課教師作為批注使用
  • 避免過多的中英文混用,已有明確翻譯詞彙者,例如「鏈結串列」(linked list) 和「佇列」(queue),就使用該中文詞彙,英文則留給變數名稱、人名,或者缺乏通用翻譯詞彙的場景
  • 在中文敘述中,使用全形標點符號,例如該用「,」,而非 ","。注意書名號的使用,即 ,非「小於」和「大於」符號
  • 避免使用不必要的 emoji 字元

開發環境

$ uname -a Linux ting-ASUS-TUF-Gaming-A15-FA506IU-FA506IU 6.8.0-52-generic #53~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Wed Jan 15 19:18:46 UTC 2 x86_64 x86_64 x86_64 GNU/Linux $ ldd --version ldd (Ubuntu GLIBC 2.35-0ubuntu3.9) 2.35 $ gcc --version gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0 $ lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Address sizes: 44 bits physical, 48 bits virtual Byte Order: Little Endian CPU(s): 16 On-line CPU(s) list: 0-15 Vendor ID: AuthenticAMD Model name: AMD Ryzen 7 4800H with Radeon Graphics CPU family: 23 Model: 96 Thread(s) per core: 2 Core(s) per socket: 8 Socket(s): 1 Stepping: 1 Frequency boost: enabled CPU max MHz: 2900.0000 CPU min MHz: 1400.0000 BogoMIPS: 5789.20

開發指定的佇列操作

q_new

原版本:

Commit b453915

使用記憶體分配來創建一個新的雙向鏈結的head
該函數分配記憶體並初始化鏈表,將 head->nexthead->prev 設置為指向自身。

如果記憶體分配失敗,返回 NULL 以防止空指標存取。

struct list_head *q_new()
{
    struct list_head *head =
        (struct list_head *) malloc(sizeof(struct list_head));
    if (!head)
        return NULL;
    head->next = head;
    head->prev = head;
    return head;
}

改進:

Commit e17b5df

使用 list.h中提供的API改寫,使程式更為簡潔。

struct list_head *q_new()
{
    struct list_head *head =
        (struct list_head *) malloc(sizeof(struct list_head));

    if (!head)
        return NULL;

    INIT_LIST_HEAD(head);
    return head;
}

q_free

Commit 52fd5d0

在佇列的實現中,鏈結串列是通過在 element_t 中定義的 list_head 結構來連接的。因此,可以使用在 list.h中定義的 list_for_each_entry_safe 遍歷佇列中的所有 element_t,並獲取每個 element_t 的位址以釋放其記憶體。此外,由於 element_t 中的字串所指向的記憶體區塊也需要釋放,最後,還需要釋放 headlist_head 所指向的記憶體區塊。

void q_free(struct list_head *head)
{
    if (!head)
        return;
    if (list_empty(head)) {
        free(head);
        return;
    }
    struct list_head *node, *safe;
    list_for_each_safe (node, safe, head) {
        element_t *current = list_entry(node, element_t, list);
        q_release_element(current);
    }
    free(head);
}

q_insert_head

原版本:

Commit a5405a3

使用 list_add 實現 q_insert_head(),將新節點插入佇列的 head 。確保當 element_t 的值指向字串時,使用 strdup() 來分配並複製字串,以避免在測試過程中發生錯誤。

bool q_insert_head(struct list_head *head, char *s)
{
    element_t *new_node = (element_t *) malloc(sizeof(element_t));
    
    if (!new_node) {
        return false;
    }
    
    new_node->value = strdup(s);
    list_add(&new_node->list, head);
    
    return true;
}

改進:

Commit 4af6ea3

這段改進優化了記憶體管理和錯誤處理。首先,新增了對 heads 是否為空指標的檢查,防止空指標錯誤。接著,將字串的記憶體分配方式改為手動分配並使用 strncpy 進行字串複製,以避免緩衝區溢出。最後,對記憶體分配失敗的情況做了處理,確保在失敗時釋放已分配的記憶體,防止記憶體洩漏。

bool q_insert_head(struct list_head *head, char *s)
{
    if (!head || !s)
        return false;

    element_t *element = malloc(sizeof(element_t));

    if (!element)
        return false;

    int s_len = strlen(s);

    element->value = (char *) malloc((s_len + 1) * sizeof(char));

    if (!element->value) {
        free(element);

        return false;
    }

    strncpy(element->value, s, (s_len + 1));
    list_add(&element->list, head);

    return true;
}

q_insert_tail

原版本:

Commit 5c91773

實現 q_insert_tail 函數,將元素插入佇列的尾部。
為新節點分配記憶體並使用 strdup 函數複製字串。
使用 list.h 的API list_add_tail() 將新節點插入佇列的末尾。

bool q_insert_tail(struct list_head *head, char *s)
{
    element_t *new_node = (element_t *) malloc(sizeof(element_t));
    if (!new_node) {
        return false;
    }
    list_add_tail(&new_node->list, head);
    new_node->value = strdup(s);

    return true;
}

改進:

Commit b81c0ad

通過利用雙向鏈結串列的特性使用 q_insert_head。將新節點插入到 head->prev 位置,等同於將節點添加至尾部。

bool q_insert_tail(struct list_head *head, char *s)
{
    if (!head || !s) {
        return false;
    }

    return q_insert_head(head->prev, s);
}

q_remove_head

q_remove_tail

q_size

Commit 35bb7b7

使用 list.h 中的 list_for_each 來計算佇列的長度。
如果佇列為空,返回 0;否則,返回元素的數量。

int q_size(struct list_head *head)
{
    if (!head)
        return 0;
    
    int len = 0;
    struct list_head *li;
    list_for_each (li, head)
        len++;
    return len;
}

q_delete_mid

q_delete_dup

q_swap

q_reverse

q_reverseK

q_sort

q_ascend

q_descend

q_merge

make test


原版本:


改進: