文章如果有寫的不對的地方,歡迎指正^^
雙向連結串列也叫雙連結串列,是連結串列的一種,它的每個資料結點中都有兩個指標,分別指向直接後繼和直接前驅。所以,從雙向連結串列中的任意一個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。一般我們都構造雙向迴圈連結串列。雙連結串列wiki
通過雙連結串列可以快速的瞭解c語言中的指標和記憶體的關係
細節
-
指標變數儲存的是記憶體的地址,可以理解為一個儲存記憶體地址的符號
-
宣告的是型別,定義的是變數。宣告的時候不分配記憶體,
int i;
屬於定義。 -
結構體宣告時不分配記憶體,定義變數的時候才分配
struct linked_list link_1;
-
指標變數相互賦值,傳遞的是所代表的記憶體地址
-
函式接受字串的時候,形參要用
char *
來代表字串陣列的首地址 -
->
用來表示呼叫結構體指標所代表的變數的域,如果用.
則代表結構體變數呼叫自己的域 -
判斷一個指標是否為空,用
== NULL
連結串列實現方案
/*
describe linked_list
_
| |__ _ _ __ _
| `_ | | | |/ _` |
| |_) | |_| | (_| |
|_.__/ \__,_|\__, |
|___/
*/
#include <stdio.h>
#include <stdlib.h>
struct linked_list {
struct linked_list *next, *prev;
char *title;
};
/* 宣告結構體指標變數,會在記憶體中開闢空間,儲存指標 */
struct linked_list * current,* prev, *head;
void list_add(char *);
void list_search();
void list_reverse_search();
int main(void) {
list_add("-");
list_add("--");
list_add("---");
list_add("----");
list_add("-----");
list_search();
list_reverse_search();
}
void list_add(char *title) {
/* 將新申請的記憶體的首地址給結構體指標變數 */
current = (struct linked_list *)malloc(sizeof(struct linked_list));
/* 初始化當前結構體,新增元素的next始終是NULL */
current->next = NULL;
current->title = title;
/* prev元素是否已經存在了,是否存在前輩元素,來判斷是否是首次新增元素*/
if(prev == NULL) {
/* 如果不存在說明,current是第一個新增進來的元素 */
current->prev = NULL;
/* 設定為首元素,這裡current和head現在指向的是同一個記憶體地址 */
head = current;
}else {
/* 如果存在前輩元素,將當前元素的prev設定為前輩元素的地址,並且將前輩元素的next設定為當前元素的地址 */
current->prev = prev;
prev->next = current;
}
/* 將當前指標所代表元素的地址,交給prev指標符號 ,現在head和prev指向同一個地址*/
prev = current;
/* 首次新增元素head是沒有next的,第二次新增的時候,prev 的next設定為了新增元素地址,所以head的next也發生了改變 ( 新增第2個元素的時候,才會重寫上個元素的next值)*/
}
/* 連結串列正序搜尋 */
void list_search() {
while(head) {
printf("%s
", head->title);
head = head->next;
}
}
/* 倒序搜尋 */
void list_reverse_search() {
while(current) {
printf("%s
", current->title);
current = current->prev;
}
}
終端向連結串列新增元素
我們來強化一下,通過終端來不停地向連結串列中新增元素。並且每次回車之後,都列印出整個列表的元素。
要點
-
宣告定義字串陣列的時候,值不能用變數
-
我們要求每次都要儲存填寫的元素值,所以每次都需要重新開闢一個記憶體空間,給新的struct,由於個struct中都儲存的是title的指標,所以新增加的元素的title也需要重新開闢記憶體空間來儲存。
-
由於每次都需要進行列印連結串列,所以
head
要注意不能被覆寫。所以重新定義一個struct linked_list
變數來做一箇中間變數。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct linked_list {
struct linked_list *next, *prev;
char *title;
};
struct linked_list *head,*current,*above;
struct linked_list head_cp;
void list_add(char *);
void list_search();
int main(int argc, char *argv[]) {
char title[10];
while(scanf("%s",title) > 0) {
list_add(title);
/* 每次新增到連結串列之後,都會進行列印整個連結串列 */
list_search();
}
/*
這樣初始化是錯誤的
error: array initializer must be an initializer list or string literal
“陣列的初始化,必須是一個列表或者是字面的字串”
char title[] = *(argv+1);
*/
}
void list_add(char *t) {
/* 分配記憶體 */
current = (struct linked_list *)malloc(sizeof(struct linked_list));
/* title 是一個記憶體空間的首地址,這裡重新分配記憶體的原因是,每次scanf都會重寫t指標下的內容,導致整個鏈式裡面所有的title都發生變化 */
char *title = (char *)malloc(sizeof(*t));
/* 將t指標指向的內容copy到title指標指向的記憶體中 */
strcpy(title,t);
/* 初始化結構體域 */
current->prev = NULL;
current->next = NULL;
/* 指向新記憶體中的title */
current->title = title;
if(above == NULL) {
head = current;
}else {
current->prev = above;
above->next = current;
}
above = current;
}
void list_search() {
/* 這將head指向的內容賦值給新開闢的結構體變數,這裡不是給的地址,給的是內容 */
head_cp = *head;
while(head_cp.title) {
printf(">>%s
", head_cp.title);
if(head_cp.next != NULL) {
head_cp = *(head_cp.next);
}else {
return;
}
}
}
操作結果
1 /* 回車 */
>>1
2 /* 回車 */
>>1
>>2
3 /* 回車 */
>>1
>>2
>>3
4 /* 回車 */
>>1
>>2
>>3
>>4
能不能不手動分配title的記憶體空間呢?
當然可以。下面程式碼中我們修改了struct
的結構將原來的char *title
修改為char title[10]
,這樣每次定義結構體變數的時候,記憶體作為struct的一部分一起分配好了。
那麼我們在add元素的時候,就需要將scanf獲取到的變數,通過strcpy(current->title,title)
的方式,寫入到char title[10]
開闢的記憶體空間中。
還需要修改list_search
方法中的while
判斷,不能再判斷指標是否為NULL的方式來判斷是否存在title內容,而是要判斷字串的長度strlen(head_cp.title) == 0
。
但是最後一種修改結構體的方式無法動態分配記憶體,每次生成的字串的長度都是10
,會浪費記憶體。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct linked_list {
struct linked_list *next, *prev;
char title[10];
};
struct linked_list *head,*current,*above;
struct linked_list head_cp;
void list_add(char *);
void list_search();
int main(int argc, char *argv[]) {
char title[10];
while(scanf("%s",title) > 0) {
list_add(title);
/* 每次新增到連結串列之後,都會進行列印整個連結串列 */
list_search();
}
/*
這樣初始化是錯誤的
error: array initializer must be an initializer list or string literal
“陣列的初始化,必須是一個列表或者是字面的字串”
char title[] = *(argv+1);
*/
}
void list_add(char *title) {
/* 分配記憶體 */
current = (struct linked_list *)malloc(sizeof(struct linked_list));
/* 初始化結構體域 */
current->prev = NULL;
current->next = NULL;
/* 這裡current->title是字串陣列的首地址,是個指標 */
strcpy(current->title,title);
if(above == NULL) {
head = current;
}else {
current->prev = above;
above->next = current;
}
above = current;
}
void list_search() {
/* 這將head指向的內容賦值給新開闢的結構體變數,這裡不是給的地址,給的是內容 */
head_cp = *head;
while(strlen(head_cp.title) != 0) {
printf(">>%s
", head_cp.title);
if(head_cp.next != NULL) {
head_cp = *(head_cp.next);
}else {
return;
}
}
}