文章如果有写的不对的地方,欢迎指正^^
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。双链表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\n", head->title);
head = head->next;
}
}
/* 倒序搜索 */
void list_reverse_search() {
while(current) {
printf("%s\n", 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\n", 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\n", head_cp.title);
if(head_cp.next != NULL) {
head_cp = *(head_cp.next);
}else {
return;
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。