C語言實現標準雙向鏈表
本文博客鏈接:http://blog.csdn.net/jdh99,作者:jdh,轉載請註明.
說明
本文使用C語言實現了雙向鏈表,可以存儲任意類型的數據。指針類型使用了標準庫中類型intptr_t,可以兼容32位和64位系統。
本文將鏈表的結構封裝在list.c中,對外提供了一些操作API,並可以傳入任意結構的數據到鏈表中。這樣提高了代碼的抽象性和複用性,並簡化了操作。
注意
鏈表涉及到指針操作,操作不當易導致內存泄漏。本文雖做了一定程度的封裝,但應用到項目中,還是要閱讀源碼理解後再安全的使用。
源碼
list.h
/**
* Copyright (c) 2019-2019 jdh99 All rights reserved.
* @file list.h
* @brief 雙向鏈表頭文件
* @verbatim
* Change Logs:
* Date Author Notes
* 2019-03-04 jdh 新建
* @endverbatim
*/
#ifndef LIST_H_
#define LIST_H_
#include "stdio.h"
#include "stdbool.h"
#include "stdint.h"
#include "stdlib.h"
#include "string.h"
/**
* @brief 創建雙向鏈表
* @return 雙向鏈表索引
*/
intptr_t list_create(void);
/**
* @brief 是否爲空
* @param index:雙向鏈表索引
* @return true:空.false:非空
*/
bool list_is_empty(intptr_t index);
/**
* @brief 獲得節點中的數據
* @note 注意本函數獲取的是數據.傳入的指針必須要先開闢好足夠空間
* @param node_index: 節點索引
* @param data_ptr: 讀取的數據指針.讀取的數據會複製到其中
* @return 0: 無此節點或者無數據.其他: 數據字節數
*/
uint32_t list_get_data(intptr_t node_index, uint8_t *data_ptr);
/**
* @brief 獲得節點中的數據指針
* @note 注意本函數獲取的是數據指針
* @param node_index: 節點索引
* @param data_ptr: 二維指針.存儲數據指針
* @return 0: 無此節點或者無數據.其他: 數據字節數
*/
uint32_t list_get_data_ptr(intptr_t node_index, uint8_t **data_ptr);
/**
* @brief 刪除鏈表
* @param index:雙向鏈表索引的地址
*/
void list_drop(intptr_t *index_ptr);
/**
* @brief 刪除所有節點
* @param index:雙向鏈表索引
*/
void list_clear(intptr_t index);
/**
* @brief 刪除節點
* @param index:雙向鏈表索引
* @param node_index: 節點索引
*/
void list_remove(intptr_t index, intptr_t node_index);
/**
* @brief 創建空節點
* @param data_size:數據大小.單位:字節
* @return 節點索引
*/
intptr_t list_create_empty_node(uint32_t data_size);
/**
* @brief 創建節點
* @param data: 數據指針.data可以爲NULL
* @param data_size:數據大小.單位:字節
* @return 節點索引
*/
intptr_t list_create_node(uint8_t *data, uint32_t data_size);
/**
* @brief 獲取下個節點索引
* @param node_index: 節點索引
* @return NULL: 無下個節點.其他:下個節點索引
*/
intptr_t list_get_next_node(intptr_t node_index);
/**
* @brief 獲取上個節點索引
* @param node_index: 節點索引
* @return NULL: 無上個節點.其他:上個節點索引
*/
intptr_t list_get_last_node(intptr_t node_index);
/**
* @brief 在某個節點前插入
* @param index:雙向鏈表索引
* @param new_node_index: 新節點索引
* @param node_index: 節點索引
*/
void list_insert_before_node(intptr_t index, intptr_t new_node_index, intptr_t node_index);
/**
* @brief 在某個節點後插入
* @param index:雙向鏈表索引
* @param new_node_index: 新節點索引
* @param node_index: 節點索引
*/
void list_insert_after_node(intptr_t index, intptr_t new_node_index, intptr_t node_index);
/**
* @brief 獲取首節點
* @param index:雙向鏈表索引
* @return NULL: 獲取失敗.其他:首節點索引
*/
intptr_t list_get_header(intptr_t index);
/**
* @brief 獲取尾節點
* @param index:雙向鏈表索引
* @return NULL: 獲取失敗.其他:尾節點索引
*/
intptr_t list_get_tail(intptr_t index);
/**
* @brief 斷開鏈接
* @param index:雙向鏈表索引
* @param node_index: 節點索引
*/
void list_break_link(intptr_t index, intptr_t node_index);
/**
* @brief 在列表最前面插入
* @param index:雙向鏈表索引
* @param new_node_index: 新節點索引
*/
void list_prepend(intptr_t index, intptr_t new_node_index);
/**
* @brief 在列表最後面插入
* @param index:雙向鏈表索引
* @param new_node_index: 新節點索引
*/
void list_append(intptr_t index, intptr_t new_node_index);
#endif
list.c
/**
* Copyright (c) 2019-2019 jdh99 All rights reserved.
* @file list.h
* @brief 雙向鏈表主文件
* @author jdh
* @verbatim
* Change Logs:
* Date Author Notes
* 2019-03-04 jdh 新建
* @endverbatim
*/
#include "list.h"
#pragma pack(1)
/**
* @brief 鏈表結構
*/
typedef struct _Node
{
uint8_t *data_ptr;
uint32_t data_size;
struct _Node *next;
struct _Node *last;
} Node, *NodePtr;
/**
* @brief 鏈表根節點
*/
typedef struct _Root
{
NodePtr header;
NodePtr tail;
// 佔據字節數指的是用戶數據佔據的字節數
// 實際佔據字節數還包括鏈表本身大小佔據的字節數
uint32_t occupied_bytes;
uint32_t real_occupied_bytes;
} Root, *RootPtr;
#pragma pack()
/**
* @brief 刪除節點後清除根節點中佔據的空間
* @param root_ptr:鏈表根節點指針
* @param node_ptr: 節點指針
* @return false: 清除異常.true:清除成功
*/
static bool delete_node_occupied(RootPtr root_ptr, NodePtr node_ptr);
static void insert_fisrt_node(RootPtr root_ptr, NodePtr new_node_ptr);
/**
* @brief 創建雙向鏈表
* @return 雙向鏈表索引
*/
intptr_t list_create(void)
{
RootPtr root_ptr = (RootPtr)malloc(sizeof(Root));
root_ptr->header = NULL;
root_ptr->tail = NULL;
root_ptr->occupied_bytes = 0;
root_ptr->real_occupied_bytes = sizeof(Root);
return (intptr_t)root_ptr;
}
/**
* @brief 是否爲空
* @param index:雙向鏈表索引
* @return true:空.false:非空
*/
bool list_is_empty(intptr_t index)
{
RootPtr root_ptr = (RootPtr)index;
if (root_ptr == NULL)
{
return true;
}
return (root_ptr->header == NULL);
}
/**
* @brief 獲得節點中的數據
* @note 注意本函數獲取的是數據.傳入的指針必須要先開闢好足夠空間
* @param node_index: 節點索引
* @param data_ptr: 讀取的數據指針.讀取的數據會複製到其中
* @return 0: 無此節點或者無數據.其他: 數據字節數
*/
uint32_t list_get_data(intptr_t node_index, uint8_t *data_ptr)
{
if (node_index == (intptr_t)NULL)
{
return 0;
}
NodePtr node_ptr = (NodePtr)node_index;
memcpy(data_ptr, node_ptr->data_ptr, node_ptr->data_size);
return node_ptr->data_size;
}
/**
* @brief 獲得節點中的數據指針
* @note 注意本函數獲取的是數據指針
* @param node_index: 節點索引
* @param data_ptr: 二維指針.存儲數據指針
* @return 0: 無此節點或者無數據.其他: 數據字節數
*/
uint32_t list_get_data_ptr(intptr_t node_index, uint8_t **data_ptr)
{
if (node_index == (intptr_t)NULL)
{
return 0;
}
NodePtr node_ptr = (NodePtr)node_index;
*data_ptr = node_ptr->data_ptr;
return node_ptr->data_size;
}
/**
* @brief 刪除鏈表
* @param index:雙向鏈表索引的地址
*/
void list_drop(intptr_t *index_ptr)
{
if (index_ptr == NULL || *index_ptr == (intptr_t)NULL)
{
return;
}
list_clear(*index_ptr);
free((void *)(*index_ptr));
*index_ptr = (intptr_t)NULL;
}
/**
* @brief 刪除所有節點
* @param index:雙向鏈表索引
*/
void list_clear(intptr_t index)
{
if (index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
while (1)
{
if (root_ptr->header == NULL)
{
break;
}
list_remove(index, (intptr_t)(root_ptr->header));
}
}
/**
* @brief 刪除節點
* @param index:雙向鏈表索引
* @param node_index: 節點索引
*/
void list_remove(intptr_t index, intptr_t node_index)
{
if (index == (intptr_t)NULL || node_index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
NodePtr node_ptr = (NodePtr)node_index;
if (node_ptr->last != NULL)
{
node_ptr->last->next = node_ptr->next;
}
else
{
// 刪除的是首節點
root_ptr->header = node_ptr->next;
}
if (node_ptr->next != NULL)
{
node_ptr->next->last = node_ptr->last;
}
else
{
// 刪除的是尾節點
root_ptr->tail = node_ptr->last;
}
free(node_ptr->data_ptr);
node_ptr->data_ptr = NULL;
if (delete_node_occupied(root_ptr, node_ptr) == false)
{
// todo
}
free(node_ptr);
node_ptr = NULL;
}
/**
* @brief 刪除節點後清除根節點中佔據的空間
* @param root_ptr:鏈表根節點指針
* @param node_ptr: 節點指針
* @return false: 清除異常.true:清除成功
*/
static bool delete_node_occupied(RootPtr root_ptr, NodePtr node_ptr)
{
if (root_ptr->occupied_bytes < node_ptr->data_size)
{
return false;
}
root_ptr->occupied_bytes -= node_ptr->data_size;
root_ptr->real_occupied_bytes -= node_ptr->data_size + sizeof(Node);
return true;
}
/**
* @brief 創建空節點
* @param data_size:數據大小.單位:字節
* @return 節點索引
*/
intptr_t list_create_empty_node(uint32_t data_size)
{
NodePtr node_ptr = (NodePtr)malloc(sizeof(Node));
node_ptr->last = NULL;
node_ptr->next = NULL;
node_ptr->data_ptr = NULL;
node_ptr->data_size = 0;
if (data_size > 0)
{
node_ptr->data_ptr = malloc(data_size);
node_ptr->data_size = data_size;
memset(node_ptr->data_ptr, 0, node_ptr->data_size);
}
else
{
node_ptr->data_ptr = NULL;
node_ptr->data_size = 0;
}
return (intptr_t)node_ptr;
}
/**
* @brief 創建節點
* @param data: 數據指針.data可以爲NULL
* @param data_size:數據大小.單位:字節
* @return 節點索引
*/
intptr_t list_create_node(uint8_t *data, uint32_t data_size)
{
NodePtr node_ptr = (NodePtr)(list_create_empty_node(data_size));
if (data != NULL && data_size > 0)
{
memcpy(node_ptr->data_ptr, data, data_size);
}
return (intptr_t)node_ptr;
}
/**
* @brief 獲取下個節點索引
* @param node_index: 節點索引
* @return NULL: 無下個節點.其他:下個節點索引
*/
intptr_t list_get_next_node(intptr_t node_index)
{
if (node_index == (intptr_t)NULL)
{
return (intptr_t)NULL;
}
NodePtr node_ptr = (NodePtr)node_index;
return (intptr_t)(node_ptr->next);
}
/**
* @brief 獲取上個節點索引
* @param node_index: 節點索引
* @return NULL: 無上個節點.其他:上個節點索引
*/
intptr_t list_get_last_node(intptr_t node_index)
{
if (node_index == (intptr_t)NULL)
{
return (intptr_t)NULL;
}
NodePtr node_ptr = (NodePtr)node_index;
return (intptr_t)(node_ptr->last);
}
/**
* @brief 在某個節點前插入
* @param index:雙向鏈表索引
* @param new_node_index: 新節點索引
* @param node_index: 節點索引
*/
void list_insert_before_node(intptr_t index, intptr_t new_node_index, intptr_t node_index)
{
if (index == (intptr_t)NULL || new_node_index == (intptr_t)NULL || node_index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
NodePtr node_ptr = (NodePtr)node_index;
NodePtr new_node_ptr = (NodePtr)new_node_index;
new_node_ptr->last = node_ptr->last;
new_node_ptr->next = node_ptr;
if (node_ptr->last != NULL)
{
node_ptr->last->next = new_node_ptr;
}
else
{
// 首節點
root_ptr->header = (NodePtr)new_node_index;
}
node_ptr->last = new_node_ptr;
root_ptr->occupied_bytes += new_node_ptr->data_size;
root_ptr->real_occupied_bytes += new_node_ptr->data_size + sizeof(Node);
}
/**
* @brief 在某個節點後插入
* @param index:雙向鏈表索引
* @param new_node_index: 新節點索引
* @param node_index: 節點索引
*/
void list_insert_after_node(intptr_t index, intptr_t new_node_index, intptr_t node_index)
{
if (new_node_index == (intptr_t)NULL || node_index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
NodePtr node_ptr = (NodePtr)node_index;
NodePtr new_node_ptr = (NodePtr)new_node_index;
new_node_ptr->last = node_ptr;
new_node_ptr->next = node_ptr->next;
if (node_ptr->next != NULL)
{
node_ptr->next->last = new_node_ptr;
}
else
{
// 尾節點
root_ptr->tail = (NodePtr)new_node_index;
}
node_ptr->next = new_node_ptr;
root_ptr->occupied_bytes += new_node_ptr->data_size;
root_ptr->real_occupied_bytes += new_node_ptr->data_size + sizeof(Node);
}
/**
* @brief 獲取首節點
* @param index:雙向鏈表索引
* @return NULL: 獲取失敗.其他:首節點索引
*/
intptr_t list_get_header(intptr_t index)
{
if (index == (intptr_t)NULL)
{
return (intptr_t)NULL;
}
RootPtr root_ptr = (RootPtr)index;
return (intptr_t)(root_ptr->header);
}
/**
* @brief 獲取尾節點
* @param index:雙向鏈表索引
* @return NULL: 獲取失敗.其他:尾節點索引
*/
intptr_t list_get_tail(intptr_t index)
{
if (index == (intptr_t)NULL)
{
return (intptr_t)NULL;
}
RootPtr root_ptr = (RootPtr)index;
return (intptr_t)(root_ptr->tail);
}
/**
* @brief 斷開鏈接
* @param index:雙向鏈表索引
* @param node_index: 節點索引
*/
void list_break_link(intptr_t index, intptr_t node_index)
{
if (index == (intptr_t)NULL || node_index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
NodePtr node_ptr = (NodePtr)node_index;
if (node_ptr->last != NULL)
{
node_ptr->last->next = node_ptr->next;
}
if (node_ptr->next != NULL)
{
node_ptr->next->last = node_ptr->last;
}
node_ptr->last = NULL;
node_ptr->next = NULL;
if (delete_node_occupied(root_ptr, node_ptr) == false)
{
// todo
}
}
/**
* @brief 在列表最前面插入
* @param index:雙向鏈表索引
* @param new_node_index: 新節點索引
*/
void list_prepend(intptr_t index, intptr_t new_node_index)
{
if (index == (intptr_t)NULL || new_node_index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
if (root_ptr->header == NULL)
{
insert_fisrt_node(root_ptr, (NodePtr)new_node_index);
return;
}
list_insert_before_node(index, new_node_index, (intptr_t)(root_ptr->header));
root_ptr->header = (NodePtr)new_node_index;
}
static void insert_fisrt_node(RootPtr root_ptr, NodePtr new_node_ptr)
{
root_ptr->header = new_node_ptr;
root_ptr->tail = new_node_ptr;
root_ptr->occupied_bytes = new_node_ptr->data_size;
root_ptr->real_occupied_bytes = new_node_ptr->data_size + sizeof(Node);
}
/**
* @brief 在列表最後面插入
* @param index:雙向鏈表索引
* @param new_node_index: 新節點索引
*/
void list_append(intptr_t index, intptr_t new_node_index)
{
if (index == (intptr_t)NULL || new_node_index == (intptr_t)NULL)
{
return;
}
RootPtr root_ptr = (RootPtr)index;
if (root_ptr->header == NULL)
{
insert_fisrt_node(root_ptr, (NodePtr)new_node_index);
return;
}
list_insert_after_node(index, new_node_index, (intptr_t)(root_ptr->tail));
root_ptr->tail = (NodePtr)new_node_index;
}
測試代碼:
#include "list.h"
static void test_case0(void);
static void test_case1(void);
static void test_case2(void);
static void test_case3(void);
static void test_case4(void);
static void test_case5(void);
int main()
{
test_case0();
test_case1();
test_case2();
test_case3();
test_case4();
test_case5();
getchar();
return 0;
}
static void test_case0(void)
{
printf("-------------------->case0:尾部插入數組測試開始\n");
uint8_t buffer[10] = {0};
intptr_t list = list_create();
printf("尾部插入數據:\n");
for (uint8_t i = 0; i < 5; i++)
{
for (uint8_t j = 0; j < 10; j++)
{
buffer[j] = i;
printf("%d\t", buffer[j]);
}
printf("\n");
intptr_t node = list_create_node(buffer, 10);
list_append(list, node);
}
printf("遍歷讀取第一個節點並刪除:\n");
while (1)
{
if (list_is_empty(list) == true)
{
break;
}
intptr_t node_index = list_get_header(list);
uint32_t bytes = list_get_data(node_index, buffer);
list_remove(list, node_index);
for (uint8_t j = 0; j < bytes; j++)
{
printf("%d\t", buffer[j]);
}
printf("\n");
}
printf("-------------------->case0:測試結束\n");
}
static void test_case1(void)
{
printf("-------------------->case1:首部插入結構體測試開始\n");
struct Test1
{
int a;
int b;
};
struct Test1 test1;
intptr_t list = list_create();
printf("首部插入數據:\n");
for (uint8_t i = 0; i < 5; i++)
{
test1.a = i;
test1.b = i;
printf("a = %d b = %d\n", test1.a, test1.b);
intptr_t node = list_create_node((uint8_t *)&test1, 10);
list_prepend(list, node);
}
printf("遍歷讀取第一個節點並刪除:\n");
while (1)
{
if (list_is_empty(list) == true)
{
break;
}
intptr_t node_index = list_get_header(list);
list_get_data(node_index, (uint8_t *)(&test1));
list_remove(list, node_index);
printf("a = %d b = %d\n", test1.a, test1.b);
}
printf("-------------------->case1:測試結束\n");
}
static void test_case2(void)
{
printf("-------------------->case2:遍歷隊列測試開始\n");
uint8_t buffer[10] = {0};
intptr_t list = list_create();
printf("插入數據:\n");
for (uint8_t i = 0; i < 5; i++)
{
for (uint8_t j = 0; j < 10; j++)
{
buffer[j] = i;
printf("%d\t", buffer[j]);
}
printf("\n");
intptr_t node = list_create_node(buffer, 10);
list_append(list, node);
}
printf("從鏈表首部開始遍歷:\n");
intptr_t node_index = list_get_header(list);
while (1)
{
if (node_index == (intptr_t)NULL)
{
break;
}
uint32_t bytes = list_get_data(node_index, buffer);
for (uint8_t j = 0; j < bytes; j++)
{
printf("%d\t", buffer[j]);
}
printf("\n");
node_index = list_get_next_node(node_index);
}
printf("從鏈表尾部開始遍歷:\n");
node_index = list_get_tail(list);
while (1)
{
if (node_index == (intptr_t)NULL)
{
break;
}
uint32_t bytes = list_get_data(node_index, buffer);
for (uint8_t j = 0; j < bytes; j++)
{
printf("%d\t", buffer[j]);
}
printf("\n");
node_index = list_get_last_node(node_index);
}
printf("-------------------->case2:測試結束\n");
}
static void test_case3(void)
{
printf("-------------------->case3:1000000次寫入然後清除列表測試開始\n");
uint8_t buffer[10] = {0};
intptr_t list = list_create();
uint32_t num = 1000000;
while (num--)
{
for (uint8_t i = 0; i < 5; i++)
{
for (uint8_t j = 0; j < 10; j++)
{
buffer[j] = i;
}
intptr_t node = list_create_node(buffer, 10);
list_append(list, node);
}
list_clear(list);
if (list_is_empty(list) == false)
{
printf("測試失敗.檢測到列表非空!\n");
break;
}
}
printf("-------------------->case3:測試結束\n");
}
static void test_case4(void)
{
printf("-------------------->case4:1000000次創建然後刪除列表測試開始\n");
uint8_t buffer[10] = {0};
uint32_t num = 1000000;
while (num--)
{
intptr_t list = list_create();
for (uint8_t i = 0; i < 5; i++)
{
for (uint8_t j = 0; j < 10; j++)
{
buffer[j] = i;
}
intptr_t node = list_create_node(buffer, 10);
list_append(list, node);
}
list_drop(&list);
if (list_is_empty(list) == false)
{
printf("測試失敗.檢測到列表非空!\n");
break;
}
}
printf("-------------------->case4:測試結束\n");
}
static void test_case5(void)
{
printf("-------------------->case5:節點數據指針操作\n");
intptr_t list = list_create();
intptr_t node_index = list_create_empty_node(10);
list_append(list, node_index);
uint8_t *data_ptr = NULL;
uint32_t data_size = list_get_data_ptr(node_index, &data_ptr);
printf("節點大小:data_size = %d\n", data_size);
printf("寫入數據:\n");
for (uint8_t i = 0; i < data_size; i++)
{
data_ptr[i] = i;
printf("%d\t", data_ptr[i]);
}
printf("\n");
intptr_t node_index_get = list_get_tail(list);
uint8_t buffer[10] = {0};
uint32_t data_size_get = list_get_data(node_index_get, buffer);
printf("讀取數據:\n");
for (uint8_t i = 0; i < data_size_get; i++)
{
printf("%d\t", buffer[i]);
}
printf("\n");
printf("-------------------->case5:測試結束\n");
}
測試輸出:
-------------------->case0:尾部插入數組測試開始
尾部插入數據:
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4 4 4
遍歷讀取第一個節點並刪除:
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4 4 4
-------------------->case0:測試結束
-------------------->case1:首部插入結構體測試開始
首部插入數據:
a = 0 b = 0
a = 1 b = 1
a = 2 b = 2
a = 3 b = 3
a = 4 b = 4
遍歷讀取第一個節點並刪除:
a = 4 b = 4
a = 3 b = 3
a = 2 b = 2
a = 1 b = 1
a = 0 b = 0
-------------------->case1:測試結束
-------------------->case2:遍歷隊列測試開始
插入數據:
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4 4 4
從鏈表首部開始遍歷:
0 0 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3
4 4 4 4 4 4 4 4 4 4
從鏈表尾部開始遍歷:
4 4 4 4 4 4 4 4 4 4
3 3 3 3 3 3 3 3 3 3
2 2 2 2 2 2 2 2 2 2
1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0
-------------------->case2:測試結束
-------------------->case3:1000000次寫入然後清除列表測試開始
-------------------->case3:測試結束
-------------------->case4:1000000次創建然後刪除列表測試開始
-------------------->case4:測試結束
-------------------->case5:節點數據指針操作
節點大小:data_size = 10
寫入數據:
0 1 2 3 4 5 6 7 8 9
讀取數據:
0 1 2 3 4 5 6 7 8 9
-------------------->case5:測試結束