C語言自學攻略-認識鏈結串列

更新於 發佈於 閱讀時間約 12 分鐘

動態記憶體配置

首先,在介紹鏈結串列之前,我們必須認識動態記憶體配置與如何表示一個節點,以下為語法:

動態記憶體配置是透過標準函式庫中的 malloc來完成的,而在使用完記憶體後要記得釋放它以避免記憶體洩漏。

那要使用這些函數,我們必須引入標頭檔stdlib.h

動態記憶體配置語法

資料型別 *ptr = (資料型別 *)malloc(sizeof(資料型別) X 數量);

示範分配一個整數大小的記憶體

#include <stdio.h>
#include <stdlib.h>

int main() {
int *ptr = (int *)malloc(sizeof(int)); // 分配一個整數大小的記憶體塊
if (ptr == NULL) {
printf("記憶體分配失敗\n");
return 1;
}

*ptr = 10; // 使用動態分配的記憶體
printf("動態分配的值: %d\n", *ptr);

free(ptr); // 釋放記憶體
return 0;
}

示範動態配置一連續記憶體陣列

#include <stdlib.h>
#include <stdio.h>
int main(void)
{
int *ptr, i;
ptr=(int *) malloc(3*sizeof(int));
*ptr=12;
*(ptr+1)=35;
*(ptr+2)=140;

for(i=0; i<3; i++)
printf("*ptr+%d=%d\n",i,*(ptr+i)); //指標方式存取
free(ptr);
getchar();
return 0;
}

這邊我提一下另一個有關動態記憶體配置的函式realloc(),該函式用於調整已經動態分配的記憶體塊大小(擴展或縮小) :

有時,動態分配的記憶體不足或超出需求。

在這種情況下,我們使用realloc()函式來更改動態分配的記憶體大小。以下為語法:

ptr =realloc(ptr, newSize);
  • 在這裡,我們重新分配ptr了新的大小。

範例:

#include <stdio.h>
#include <stdlib.h>

int main() {
int *arr;
int n = 5;

// 動態分配初始記憶體
arr = (int *)malloc(n * sizeof(int));

// 初始化陣列
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}

// 顯示原始陣列
printf("原始陣列: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");

// 擴展記憶體
n = 10;
arr = (int *)realloc(arr, n * sizeof(int));

// 初始化新元素
for (int i = 5; i < n; i++) {
arr[i] = i + 1;
}

// 顯示擴展後的陣列
printf("擴展後的陣列: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");

// 釋放記憶體
free(arr);
return 0;
}

鏈結串列

鏈結串列中的每個節點包含兩個主要部分:

  1. 資料部分:存放節點的值。
  2. 指標部分:指向下一個節點的位址。

語法:

struct Node {
int data; // 資料部分
struct Node *next; // 指標部分,指向下一個 Node 節點
};

接下來示範如何使用:

#include <stdio.h>
#include <stdlib.h>

// 主函數:程式進入點
int main(void)
{
int num, i; // 宣告變數:num 用來存放學生數量,i 為迴圈計數器

// 定義結構體 student,包含學生姓名與分數兩個成員
struct student
{
char name[10]; // 用來存放學生姓名的字元陣列,最多可存 9 個字元 + '\0'
int score; // 用來存放學生分數
} *ptr; // 宣告一個指向 struct student 結構的指標 ptr

// 提示使用者輸入學生數量
printf("Number of student: ");
scanf("%d", &num); // 讀取使用者輸入的學生數量,存入變數 num

// 使用 malloc 根據學生數量動態配置記憶體,配置的空間大小為 num 個 student 結構的大小
ptr = (struct student *) malloc(num * sizeof(struct student));

// 使用迴圈逐一輸入每位學生的資料
for (i = 0; i < num; i++)
{
// 輸入學生姓名
printf("name for student %d: ", i + 1);
// 將使用者輸入的字串存入第 i 個 student 結構的 name 成員
scanf("%s", (ptr + i)->name);

// 輸入學生分數
printf("score for student %d: ", i + 1);
// 將使用者輸入的整數存入第 i 個 student 結構的 score 成員
scanf("%d", &(ptr + i)->score);
}

// 使用迴圈逐一印出所有學生的姓名與分數
for (i = 0; i < num; i++)
{
printf("%s: score=%d\n", (ptr + i)->name, (ptr + i)->score);
}

// 釋放先前使用 malloc 配置的記憶體空間,以避免記憶體洩漏
free(ptr);

return 0; // 程式正常結束
}
  • -> 是用來透過指標存取結構體成員的運算子,不要忘記喔!
  • ptr 是指向結構體的指標。
  • (ptr + i) 透過指標運算,指向學生陣列中的第 i 個元素。
  • ->name 則存取該結構中的 name 成員。

有了基本的認識,接下來就可以示範鏈結串列,這個程式碼功能為輸入節點個數,並且針對每個節點輸入資料,並在最後顯示節點的資料以及指向關係,讀者可以搭配程式碼下方的圖來了解程式流程:

#include <stdio.h>
#include <stdlib.h>

struct node
{
int data; // 資料成員
struct node *next; // 鍊結成員,存放指向下一個節點的指標
};

typedef struct node NODE; // 將 struct node 定義成 NODE 型態

int main(void)
{
int i, val, num;
NODE *first, *current, *previous; // 建立 3 個指向 NODE 的指標
printf("Number of nodes: ");
scanf("%d", &num); // 輸入節點的個數

for (i = 0; i < num; i++)
{
current = (NODE *) malloc(sizeof(NODE)); // 建立新的節點
if (current == NULL)
{
printf("Memory allocation failed\n");
return 1; // 若分配失敗,結束程式
}
printf("Data for node %d: ", i + 1);
scanf("%d", &(current->data)); // 輸入節點的 data 成員
if (i == 0)
first = current; // 如果是第一個節點,把標示 first 指向目前的節點
else
previous->next = current; // 把前一個節點的 next 指向目前的節點
current->next = NULL; // 把目前節點的 next 指向 NULL
previous = current; // 把前一個節點設成目前的節點
}

current = first; // 設定 current 為第一個節點
while (current != NULL) // 如果還沒有到串列末端,則進行走訪的動作
{
printf("address=%p, ", (void *)current); // 印出節點的位址
printf("data=%d, ", current->data); // 印出節點的 data 成員
printf("next=%p\n", (void *)current->next); // 印出節點的 next 成員
previous = current; // 使用 previous 暫存當前節點
current = current->next; // 設定 current 指向下一個節點
free(previous); // 釋放當前節點
}

// 將 沒用到的指針 指向 NULL,避免成為野指標(current最後值已經是 NULL,不用再設定)
first = NULL;
previous = NULL;

return 0;
}

節點創建流程圖示:

raw-image

節點走訪流程圖示:

raw-image
在當今數位時代,電資領域人才需求爆發式成長,不論是前端網頁設計、嵌入式開發、人工智慧、物聯網還是軟硬體整合,這些技術都在改變世界。而掌握 C/C++、Python、數位邏輯、電路學與嵌入式開發等大學電資領域的課程,正是進入這個高薪、高需求產業的關鍵!
留言
avatar-img
留言分享你的想法!

































































本章將深入解析 C 語言的檔案處理 ,學習如何使用 標準 I/O 函式 (fopen(), fclose(), fgets(), fprintf()等) 來讀取與寫入檔案。檔案處理是 儲存與管理數據 的核心技術,適用於日誌記錄等應用。 透過本章學習,你將能夠 熟練操作 C 語言的檔案處理
C Preprocessor 負責在編譯之前對原始程式碼進行處理。其所執行的一些動作包刮了: 將其它檔案含入編譯的檔案中、定義符號常數和巨集、程式碼的條件式編譯、以及條件式執行的前置處理器命令。所有前置處理器命令都會以# 開始。本章會讓你對前置處理器有一些初步的認識與了解。
上節我們有提到過指標陣列,即陣列元素是指標,這與「指向陣列的指標」不同,千萬不要混淆。 今天這節要來講函式指標、指標型態轉換。
本篇文章深入淺出地介紹 C 語言中的資料型別,包含 enum、struct、union 和 typedef 的使用方法與應用情境。並透過圖解說明 struct 的記憶體配置和 union 的特性,以及位元組順序 (Endianness) 的概念。文末並附帶練習題,幫助讀者理解相關知識。
這篇文章深入淺出地解釋了 C 語言中指標陣列的概念,並透過範例程式碼和練習題幫助讀者理解指向指標的指標(char **)、指標陣列(char *[])等等的觀念與用法。文章還包含了指標常數(char * const)與常數指標(const char * const)的練習題,提升讀者的學習成效。
本章將介紹 C 語言的字串指標,讓讀者對於此概念有正確的理解。
本章將深入解析 C 語言的檔案處理 ,學習如何使用 標準 I/O 函式 (fopen(), fclose(), fgets(), fprintf()等) 來讀取與寫入檔案。檔案處理是 儲存與管理數據 的核心技術,適用於日誌記錄等應用。 透過本章學習,你將能夠 熟練操作 C 語言的檔案處理
C Preprocessor 負責在編譯之前對原始程式碼進行處理。其所執行的一些動作包刮了: 將其它檔案含入編譯的檔案中、定義符號常數和巨集、程式碼的條件式編譯、以及條件式執行的前置處理器命令。所有前置處理器命令都會以# 開始。本章會讓你對前置處理器有一些初步的認識與了解。
上節我們有提到過指標陣列,即陣列元素是指標,這與「指向陣列的指標」不同,千萬不要混淆。 今天這節要來講函式指標、指標型態轉換。
本篇文章深入淺出地介紹 C 語言中的資料型別,包含 enum、struct、union 和 typedef 的使用方法與應用情境。並透過圖解說明 struct 的記憶體配置和 union 的特性,以及位元組順序 (Endianness) 的概念。文末並附帶練習題,幫助讀者理解相關知識。
這篇文章深入淺出地解釋了 C 語言中指標陣列的概念,並透過範例程式碼和練習題幫助讀者理解指向指標的指標(char **)、指標陣列(char *[])等等的觀念與用法。文章還包含了指標常數(char * const)與常數指標(const char * const)的練習題,提升讀者的學習成效。
本章將介紹 C 語言的字串指標,讓讀者對於此概念有正確的理解。
你可能也想看
Google News 追蹤
Thumbnail
這篇內容,將會講解什麼是陣列,以及與陣列相關的知識。包括陣列的簡介、陣列的資料限制、陣列的維度、一維陣列、二維陣列。
此篇文章連結 RAM 與 C語言陣列的關係並提供陣列與for-loop 使用的相關教學 前半段為基本電腦觀念、後半段為實作能力的教學
Thumbnail
本章節的目的是讓讀者瞭解C#的物件導向特性,包括類別、繼承、多型、封裝等基本概念,以及介面、抽象類別、靜態類別等進階主題。此外,本章節也將介紹如何使用列舉、委派、Lambda表達式、泛型及反射,這些都是C#中常見的強大功能。
Thumbnail
本章節旨在介紹 C# 中函數的基本結構,包括訪問修飾符、返回類型、方法名稱、參數列表和方法體。同時,也介紹了函數的各種呼叫方式、參數傳遞方式和返回值類型。讀者可以通過本章節,深入理解 C# 中函數的使用和應用。
Thumbnail
C#程式由一或多個檔案組成,包含命名空間、類別、結構、介面、列舉和委派等型別。Main方法是C#應用程式的進入點。在C#中,註解用於在程式碼中添加說明,有單行和多行兩種類型。變數的定義需要指定變數的類型和名稱,可以一次為多個變數賦值。
Thumbnail
可選串聯(?.)運算符用於訪問 object 的屬性或調用函數。如果使用該運算符訪問的object 或調用的函式為 undefined 或 null,則表達式會回傳 undefined,而不是拋出錯誤。
Thumbnail
本文介紹了串列運算式的應用,以及與Lambda匿名函式方法的比較,並提供了程式範例。串列運算式提供了一種簡潔的語法,用於創建、轉換和過濾列表。lambda函式用於創建匿名函式,通常用於簡單的操作。建議在比較複雜的情況下使用一般for迴圈加if來表示。
Thumbnail
介紹C++ 語法 資料型態,架構說明 程式語言為人類與電腦溝通的工具 程式設計流程: 定義問題 -> 問題分析 -> 撰寫演算法 ->程式撰寫 -> 程式執行及維護
本課程學習如何使用 ConstraintLayout 約束佈局中的 Chains 鏈結屬性。
Thumbnail
這篇內容,將會講解什麼是陣列,以及與陣列相關的知識。包括陣列的簡介、陣列的資料限制、陣列的維度、一維陣列、二維陣列。
此篇文章連結 RAM 與 C語言陣列的關係並提供陣列與for-loop 使用的相關教學 前半段為基本電腦觀念、後半段為實作能力的教學
Thumbnail
本章節的目的是讓讀者瞭解C#的物件導向特性,包括類別、繼承、多型、封裝等基本概念,以及介面、抽象類別、靜態類別等進階主題。此外,本章節也將介紹如何使用列舉、委派、Lambda表達式、泛型及反射,這些都是C#中常見的強大功能。
Thumbnail
本章節旨在介紹 C# 中函數的基本結構,包括訪問修飾符、返回類型、方法名稱、參數列表和方法體。同時,也介紹了函數的各種呼叫方式、參數傳遞方式和返回值類型。讀者可以通過本章節,深入理解 C# 中函數的使用和應用。
Thumbnail
C#程式由一或多個檔案組成,包含命名空間、類別、結構、介面、列舉和委派等型別。Main方法是C#應用程式的進入點。在C#中,註解用於在程式碼中添加說明,有單行和多行兩種類型。變數的定義需要指定變數的類型和名稱,可以一次為多個變數賦值。
Thumbnail
可選串聯(?.)運算符用於訪問 object 的屬性或調用函數。如果使用該運算符訪問的object 或調用的函式為 undefined 或 null,則表達式會回傳 undefined,而不是拋出錯誤。
Thumbnail
本文介紹了串列運算式的應用,以及與Lambda匿名函式方法的比較,並提供了程式範例。串列運算式提供了一種簡潔的語法,用於創建、轉換和過濾列表。lambda函式用於創建匿名函式,通常用於簡單的操作。建議在比較複雜的情況下使用一般for迴圈加if來表示。
Thumbnail
介紹C++ 語法 資料型態,架構說明 程式語言為人類與電腦溝通的工具 程式設計流程: 定義問題 -> 問題分析 -> 撰寫演算法 ->程式撰寫 -> 程式執行及維護
本課程學習如何使用 ConstraintLayout 約束佈局中的 Chains 鏈結屬性。