用 Link list 建造 Queue for C
===
工欲善其事,必先利其器,首先要有:
1. Pointer 指標的觀念
2. Struct 結構的觀念
3. Dynamic memory allocation 的使用
4. 用 link list 製作 queue
---
## 關於指標的使用
\*Q 在宣告中出現與程式碼中出現,有兩種不同的功能
在宣告時,用了 int \*Q 表示宣告指標變數
\*Q 從指標指向的位址內容,或是修改指標指向的記憶體位址的內容
Q 存放指向 mem 位址的數值
&Q Q 這個 mem 位址的數值
怎麼驗證記憶體位址,使用16進位的方式,進出變數內容,可以很明顯地看出差異
%x 資料以16進位的方式呈現
```clike=
int a;
int *x;
a = 15;
printf("&a %x x %x \n", &a, x);
x = &a;
*x = 11;
printf("&a %x a %d *x %d x %x &x %x", &a, a, *x, x, &x);
```
Output 的結果為
```
&a b4f80c54 x 0
&a b4f80c54 a 11 *x 11 x b4f80c54 &x b4f80c58
```
可以看出來 &a 印出的是 a 這個變數的記憶體位址,在程式中,把 a 的位址給了 x 指標,透過 \*x 更改指向的 mem 位址的內容,也就是對 a 的內容做修改。
更多的指標
char * 意思是指向的資料有正負號 8 bits 的 pointer
unsigned long * 無號 32 bits 的 pointer
void * 就是無型別
```
char *ptr8;
unsigned long *ptr32;
void *ptrx
ptr32 = ptr8; 這是type錯誤
ptrx = ptr8; 頂多是warning
*ptr8 = 100; 在ptr8指向的記憶體位址填入100得值
*ptrx = 100; error不知道指向的記憶體位址的目的地,空間有多大
```
---
## 關於結構的使用
使用的方法,格式是
```clike=
struct 結構名稱{
資料型態 資料變數名稱;
資料型態 資料變數名稱;
...
}
```
使用自己定義資料結構時,可以使用這種方式
```clike=
struct node Node1;
```
建立一個 link list ,我們需要內容跟連結,所以使用方法如下
```clike
struct node{
int val;
struct node *link
}
```
其中 \*link 的 `*` 不可省略。
原因是 `struct node link` 就變成遞迴定義,也就是自己呼叫自己這種結構,問題會發生在 compiler 無法判斷這個 `struct node` 會呼叫多少次,無從得知 `struct node` 的大小。
而我們可以使用 `.` 這種運算子,取出 struct 中的元素。延續上面的宣告,我們可以這樣建立對應的 struct 變數
```clike
struct node Node1, Node2, Node3;
Node1.val = 100;
Node2.val = 50;
Node1.link = &Node2;
Node2.link = &Node3;
```
依此類推,想透過 Node1 間接在 Node3 裡面存放資料
```clike
Node1.link.link.val = 500;
```
Node1 透過 link 連接到 Node2 ,再透過 Node2 的 link 連結到 Node3 ,再針對 Node3 中的 val 填值。
這樣使用會發生問題,因為 struct 的 `.` 運算,左邊必須是 struct ,不能是 pointer 。
所以要使用上面的存值方法,必須修改成這樣。
```clike
(* (* Node1.link).link).val = 500;
```
透過上面的使用, struct 裡面有 pointer to struct ,如果透過 pointer 的方式存取 struct 的成員,就必須對指標的觀念清楚,小心謹慎地使用 \* 和 () 來達成方法。
因為 struct 成員包含指向結構的指標(define a pointer to struct in a struct),是一種很常見的事情,但是上面的使用方式,不方便也不直覺,所以C語言定義了 `->` 運算符號。 `->` 符號的左邊是一個 pointer to struct ,右邊是該 struct 中要使用的成員。當 struct 為透過指標存取結構時,必須使用 `->` 。
`->` 為第一優先權且為左結合,上面的例子可以變成這樣。
```clike
Node1.link->link->val = 500;
```
Typedef 的介紹
替一個已知的資料型態取另外的名稱(別名),也就是 int 可以用 int32 來取代 int ,用 int32 來設定 int 的資料型態
```clike
typedef int int32;
int32 x = 3;
```
Typedef struct
```clike
typedef struct node{
float time;
struct node *link;
}Q_node;
```
可以拆解成兩部份
1. 建立 struct 為 node 這個資料結構
```clike
struct node{
float time;
struct node *link;
};
```
2. 替 struct node 這個資料結構取別一個新名字稱作 Q_node
```clike
typedef struct node Q_node;
```
使用別名同時又宣告該結構的變數供程式使用
```clike
typedef struct node{
float time;
struct node *link;
}Q_node, arrival_time;
```
實際上可分解成下面三個動作
```clike
typedef struct node{
float time;
struct node *link;
};
typedef struct node Q_node;
Q_node arrival_time;
```
延伸用法(少了 struct 的名稱)
```clike
typedef struct{
float time;
struct node *link;
}Q_node, arrival_time;
```
也就是宣告了一種結構他的變數名稱為 arrival_time ,而這種結構的別名是 Q_node
其運行的方式類似下面的方法
```clike
struct{ //實際程式中宣告 struct 沒有名稱會發生問題
float time;
struct node *link;
};
typedef struct node Q_node;
Q_node arrival_time;
```
在運行的過程中嘗試做鏈結串列會發生問題。
```clike=
Q_node *node1 = (Q_node *) malloc(sizeof(Q_node));
Q_node *node2 = (Q_node *) malloc(sizeof(Q_node));
node1->time = 5;
node1->link = node2;
```
會發生型態不相容的問題
Q_node* 無法對應到 struct 中的 struct node *
```clike=
typedef struct node{
float time;
struct node *link;
}Q_node;
```
需要給予 struct 名稱宣告鏈結時才能被對應。
---
## 動態記憶體分配
程式在執行時才獲得的記憶體空間,由於這種記憶體空間的配置方式使用 Heap(堆疊) 方式來達成,也有人將這塊記憶體空間稱謂 Heap ,在程式結束時,針對資料的釋放,採用後進先出的方式釋放資源。
首先程式要先告訴電腦需要多少空間, C 語言使用 malloc() ,實現動態記憶體分配。
有分配就會有歸還, C 語言使用 free() 歸還程式執行時, malloc() 所分配的記憶體空間。
void *malloc(size_t size);
void free(void *ptr);
實際使用的方法
```
資料結構 *指標;
指標 = (資料型態 *) malloc (所需要的記憶體空間,他的bytes數);
```
malloc() 成功時,會回傳的是 void * 所指向的記憶體位址,若失敗則會回傳 NULL 。
所以前面要有一個 Datatype * 去轉換成對應的指標型態,我們稱為轉型(casting or type casting),將程式獲得的記憶體位址存入宣告的指標變數。
```clike=
Datatype *ptr;
ptr = (Datatype *) malloc (sizeof(指標資料結構的大小)*(空間大小));
```
```
free(void 釋放分配出去的記憶體空間,他對應的記憶體位址);
```
```clike=
free(ptr);
```
[更多 C memory alloction function 的介紹](https://openhome.cc/Gossip/CGossip/MallocFree.html)
在 C++ 裡面動態配置記憶體空間,則是使用 new 跟 delete
```
資料型態* 指標;
指標 = new 資料型態[所需要的記憶體空間,他的bytes數];
delete [] 指標;
```
```clike=
Datatype* ptr;
ptr = new Datatype[所需要的記憶體空間,他的bytes數];
delete [] ptr;
```
ex:
```clike=
User* userArray;
userArray = new User[5];
delete [] userArray;
```
[關於 C++ new 更多的介紹](http://www.cplusplus.com/reference/new/operator%20new[]/)
[關於 C++ delete 更多的介紹](http://www.cplusplus.com/reference/new/operator%20delete[]/)
---
## Linked list
建立 single linked list
```c=
typedef struct __node {
int value;
struct __node *next;
} node_t;
```
產生 linked list
```c=
static node_t *list_make_node_t(node_t *list, int val) {
node_t *tmp = list;
node_t *n = malloc(sizeof(node_t));
n->value = val;
n->next = NULL;
if (!list)
return n;
while (tmp->next)
tmp = tmp->next;
tmp->next = n;
return list;
}
```
釋放 linked list
```c=
static void list_free(node_t **list) {
node_t *pNode;
while (*list) {
pNode = *list;
*list = ((*list)->next);
free(pNode);
}
}
```
將 node 加入 linked list 中
```c=
static inline void list_add_node_t(node_t **list, node_t *node_t) {
node_t->next = *list;
*list = node_t;
}
```
將兩個 linked list 合併
```c=
static inline void list_concat(node_t **left, node_t *right) {
while (*left)
left = &((*left)->next);
*left = right;
}
```
---
## 用簡單的 linked list 製造 FIFO queue
首先要有 linked list 的 struct
```clike=
typedef struct node{
float time;
struct node *link;
}Q_node;
```
接著宣告可以存放 node 的 queue
```clike=
typedef struct queue{
int size;
Q_node *head, *tail;
}Queue;
```
一開始一定要執行的初始化
```clike=
void initQueue(Queue *q){
q->size = 0;
q->head = q->tail = NULL;
}
```
判斷宣告出來的 queue 是否為不存在,或是確認資料沒有被放進 queue 裡面
```clike=
int queueIsEmpty(Queue *q){
return q->head == NULL;
}
```
將資料放入 node,將 node 加入 queue 中
```clike=
void addQueue(Queue *q, float arrival_time){
Q_node *temp = (Q_node *)malloc(sizeof(Q_node));
temp->time = arrival_time;
temp->link = NULL;
if(q->head == NULL)
q->head = temp;
else
q->tail->link = temp;
q->tail = temp;
q->size++;
}
```
取出 node 中的資料,並把該 node 從 queue 中移除
```clike=
float deleteQueue(Queue *q){
Q_node *temp = q->head;
float retval = temp->time;
q->head = (q->head)->link;
if(q->head == NULL)
q->tail = NULL;
q->size--;
free(temp);
return retval;
}
```
----
## 沒用的冷知識-指標宣告的表示方式
### Typical C programmer
**int \*p**
It explains "\*p is what is the int" emphasizing **syntax**.
ex.
int \*p1
It means "\*p1 is what is the int".
### Typical C++ programmer
**int\* p**
It explains "p is a pointer to an int" emphasizing **type**.
ex.
int\* p1
It means "p1 is a pointer points to an integer".
簡單的說:
int \*p; 表示 \*p 是一個 int。(C style)
int* p; 表示 p 是一個指向 int 的指標,p 的類型是 int*。(C++ style)
結論:不論你用 C 或 C++ 的方式宣告指標變數,編譯器都能看懂程式碼中的指標宣告,唯一的差別只是大部分人不會注意到的意義。
[參考來源](http://www.stroustrup.com/bs_faq2.html#whitespace)
---
###### tags: `C` `Queue`