C語言鏈表詳解及代碼分析
什么是鏈表
鏈表是一種常見的重要的數(shù)據(jù)結(jié)構(gòu)。它是動態(tài)地進(jìn)行存儲分配的一種結(jié)構(gòu)。鏈表和數(shù)組比較,不用事先確定存儲空間,而是根據(jù)需要開辟內(nèi)存單元。
下圖1是最簡單的一種鏈表(單向鏈表)的結(jié)構(gòu)

第 0 個(gè)結(jié)點(diǎn)稱為頭結(jié)點(diǎn),它存放有第一個(gè)結(jié)點(diǎn)的首地址,它沒有數(shù)據(jù),只是一個(gè)指針變量。以下的每個(gè)結(jié)點(diǎn)都分為兩個(gè)域,一個(gè)是數(shù)據(jù)域,存放各種實(shí)際的數(shù)據(jù),如學(xué)號 num,姓名 name,性別 sex 和成績 score 等。另一個(gè)域?yàn)橹羔樣?,存放下一結(jié)點(diǎn)的首地址。鏈表中的每一個(gè)結(jié)點(diǎn)都是同一種結(jié)構(gòu)類型。
環(huán)境構(gòu)建
用的Visual Studio 2019軟件



在源文件中添加C文件

建立靜態(tài)鏈表
包含所需要的頭文件
#include<stdio.h> //標(biāo)準(zhǔn)輸入輸出頭文件 #include<stdlib.h>//包含了C、C++語言的最常用的系統(tǒng)函數(shù)
宏定義相關(guān)變量
#define LEN sizeof(struct Student)//宏定義節(jié)點(diǎn)長度得命名 #define TYPE struct Student//宏定義結(jié)構(gòu)體變量命名
創(chuàng)建一個(gè)結(jié)構(gòu)體
struct Student//定義一個(gè)學(xué)生類型結(jié)構(gòu)體,包括學(xué)號,分?jǐn)?shù)
{
long num;
float score;
struct Student* next;//next是指針變量,指向結(jié)構(gòu)體變量
};
//指向結(jié)構(gòu)體對象得指針變量既可以指向結(jié)構(gòu)體變量,也可以指向結(jié)構(gòu)體數(shù)組中得元素
主函數(shù)
int main()
{
TYPE* head,*p;//定義頭指針
struct Student a,b,c;//定義三個(gè)結(jié)構(gòu)體變量
a.num = 101; a.score = 20;//分別對三個(gè)結(jié)點(diǎn)賦值
b.num = 102; b.score = 20;
c.num = 103; c.score = 20;
/*1、A.B則A為對象或者結(jié)構(gòu)體
2、A->B則A為指針,->是成員提取,A->B是提取A中的成員B,A只能是指向類、結(jié)構(gòu)、聯(lián)合的指針;*/
head = &a;
a.next = &b;
b.next = &c;
c.next = NULL;
p = head;//把首地址給變量
do
{
printf("%ld %5.1f\n",p->num,p->score);//輸出每個(gè)結(jié)點(diǎn)信息
p = p->next;//使P指向下一個(gè)結(jié)點(diǎn)
} while (p != NULL);//直到指針域指向空值
return 0;
}
結(jié)果展示

說明
將第一個(gè)結(jié)點(diǎn)的起始地址賦值給頭指針head,將第二個(gè)結(jié)點(diǎn)的起始地址賦值給第一個(gè)結(jié)點(diǎn)的next成員,將第二個(gè)結(jié)點(diǎn)的起始地址賦給第一個(gè)結(jié)點(diǎn)的next…第三個(gè)結(jié)點(diǎn)的next賦值為NULL,這就形成了簡單的鏈表。
建立動態(tài)鏈表
所謂建立動態(tài)鏈表是指在程序執(zhí)行過程中從無到有地建立起一個(gè) 鏈表,即一個(gè)一個(gè)地開辟結(jié)點(diǎn)和輸入各結(jié)點(diǎn)數(shù)據(jù),并建立起前后相連的關(guān)系。
包含所需要的頭文件
#include<stdio.h> //標(biāo)準(zhǔn)輸入輸出頭文件 #include<stdlib.h>//包含了C、C++語言的最常用的系統(tǒng)函數(shù) #include<malloc.h>//動態(tài)存儲分配函數(shù)頭文件
宏定義相關(guān)變量
#define LEN sizeof(struct Student)//宏定義節(jié)點(diǎn)長度得命名 #define TYPE struct Student//宏定義結(jié)構(gòu)體變量命名
創(chuàng)建一個(gè)結(jié)構(gòu)體
struct Student//定義一個(gè)學(xué)生類型結(jié)構(gòu)體,包括學(xué)號,分?jǐn)?shù)
{
long num;
float score;
struct Student* next;//next是指針變量,指向結(jié)構(gòu)體變量
};
//指向結(jié)構(gòu)體對象得指針變量既可以指向結(jié)構(gòu)體變量,也可以指向結(jié)構(gòu)體數(shù)組中得元素
建立鏈表函數(shù)
TYPE* Creat(void)//定義函數(shù),此函數(shù)返回一個(gè)指向鏈表頭的指針
{
TYPE* head;//定義頭指針
TYPE* p1,*p2;//定義兩個(gè) 指針變量用來相互保存
number = 0;//開始時(shí),結(jié)點(diǎn)清零
p1 = p2 = (TYPE*)malloc(LEN);//創(chuàng)建存儲空間
printf("請按格式輸入學(xué)生學(xué)號,分?jǐn)?shù)\n");//輸出提示信息
printf("例如101,1 并以0,0結(jié)束\n");
scanf("%ld,%f", &p1->num, &p1->score);//按格式輸入第一個(gè)結(jié)點(diǎn)的信息
head = NULL;//第一個(gè)結(jié)點(diǎn)頭指針賦空值
while (p1->num!=0)//循環(huán)直到輸入學(xué)生學(xué)號為0,就結(jié)束
{
number++;//結(jié)點(diǎn)自增
if (number == 1)//如果只有一個(gè)結(jié)點(diǎn),那么頭指針指向第一個(gè)輸入的結(jié)點(diǎn)
head = p1;
else
p2->next = p1;//如果大于1個(gè),那么要用next保存前一個(gè)結(jié)點(diǎn)的信息
p2 = p1;//保存前一個(gè)結(jié)點(diǎn)信息
p1 = (TYPE*)malloc(LEN);//開辟新的結(jié)點(diǎn)
scanf("%ld,%f", &p1->num, &p1->score);//輸入下一個(gè)結(jié)點(diǎn)信息
}
p2->next = NULL;//循環(huán)結(jié)束,將指向信息賦空值
return (head);//返回首地址
}
主函數(shù)
int main()
{
TYPE* pt;//定義一個(gè)結(jié)構(gòu)體指針變量
pt = Creat();//函數(shù)返回鏈表第一個(gè)結(jié)點(diǎn)的地址
printf("\nnum:%ld\nscore:%5.lf\n", pt->num,pt->score);//輸出第一個(gè)結(jié)點(diǎn)的成員值
return 0;
}
結(jié)果展示

== 文中最后結(jié)果顯示的是第一個(gè)結(jié)點(diǎn)的內(nèi)容,作為有強(qiáng)大功能的鏈表,對他的操作當(dāng)然有許多,比如:鏈表的創(chuàng)建,修改,刪除,插入,輸出,排序,反序,清空鏈表的元素,求鏈表的長度等等。==
鏈表的輸出
用循環(huán)直接可以輸出鏈表
輸出函數(shù)
void print(TYPE * head)
{
TYPE * p;//定義指針
printf("\nNOW These %d records are:\n");//輸出顯示信息
p = head;//使p指向第一個(gè)結(jié)點(diǎn)
if(head!=NULL)//輸出第一個(gè)結(jié)點(diǎn)后的信息
do {
printf("%ld %5.1f\n",p->num,p->score);
p = p->next;//指向下個(gè)結(jié)點(diǎn)
} while (p != NULL);
}
主函數(shù)
int main()
{
TYPE * pt;//定義一個(gè)結(jié)構(gòu)體指針變量
pt = Creat();//函數(shù)返回鏈表第一個(gè)結(jié)點(diǎn)的地址
print(pt);//輸出調(diào)用
return 0;
}
鏈表的修改
修改函數(shù)
修改鏈表節(jié)點(diǎn)值很簡單。下面是一個(gè)傳入鏈表和要修改的節(jié)點(diǎn),來修改值的函數(shù).
void change(TYPE* head, int n) //修改指定位置的結(jié)點(diǎn)的信息
{
TYPE* p = head;//傳入首地址
int i = 0;
while (i < n && p != NULL) {
p = p->next;
i++;
}//找到相應(yīng)的位置結(jié)點(diǎn)
if (p != NULL) {
printf("輸入要修改的值\n");
scanf("%ld,%f", &p->num, &p->score);//輸入下一個(gè)結(jié)點(diǎn)信息
}
else
printf("節(jié)點(diǎn)不存在\n");
}
主函數(shù)
int main()
{
TYPE* pt;//定義一個(gè)結(jié)構(gòu)體指針變量
pt = Creat();//函數(shù)返回鏈表第一個(gè)結(jié)點(diǎn)的地址
change(pt,2);//修改相關(guān)結(jié)點(diǎn)的信息,假設(shè)修改第2+1個(gè)
print(pt);//輸出調(diào)用
return 0;
}
##鏈表的刪除
刪除鏈表的元素也就是把前節(jié)點(diǎn)的指針域越過要刪除的節(jié)點(diǎn)指向下下個(gè)節(jié)點(diǎn)。即:p->next = q->next;然后放出q節(jié)點(diǎn)的空間,即free(q);

刪除函數(shù)
void delet(TYPE* head, int n) {
TYPE* p = head, * in;//定義兩邊指針
int i = 0;
while (i < n && p != NULL) {
in = p;//找到左邊的
p = p->next;//找到右邊的
i++;
}
if (p != NULL) {
in->next = p->next;//將左右鏈接
free(p);//釋放中間結(jié)點(diǎn)
}
else {
printf("節(jié)點(diǎn)不存在\n");
}
}
主函數(shù)
int main()
{
TYPE* pt;//定義一個(gè)結(jié)構(gòu)體指針變量
pt = Creat();//函數(shù)返回鏈表第一個(gè)結(jié)點(diǎn)的地址
delet(pt,1);//刪除第1+1個(gè)結(jié)點(diǎn)
print(pt);//輸出調(diào)用
return 0;
}
輸出結(jié)果

##鏈表的插入
我們可以看出來,插入節(jié)點(diǎn)就是用插入前節(jié)點(diǎn)的指針域鏈接上插入節(jié)點(diǎn)的數(shù)據(jù)域,再把插入節(jié)點(diǎn)的指針域鏈接上插入后節(jié)點(diǎn)的數(shù)據(jù)域。根據(jù)圖,插入節(jié)點(diǎn)也就是:e->next = head->next; head->next = e;
增加鏈表節(jié)點(diǎn)用到了兩個(gè)結(jié)構(gòu)體指針和一個(gè)int數(shù)據(jù)。

插入函數(shù)
void insert(TYPE* head, int n) {//鏈表的插入
TYPE* p = head, * in;
int i = 0;
while (i < n && p != NULL) {
p = p->next;
i++;//找到相應(yīng)結(jié)點(diǎn)
}
if (p != NULL) {
in = (TYPE*)malloc(sizeof(TYPE));//開辟新的空間
printf("輸入要插入的值\n");
scanf("%ld,%f", &in->num, &in->score);//輸入新的結(jié)點(diǎn)信息
in->next = p->next;//填充in節(jié)點(diǎn)的指針域,也就是說把in的指針域指向p的下一個(gè)節(jié)點(diǎn)
p->next = in;//填充p節(jié)點(diǎn)的指針域,把p的指針域重新指向in
}
else {
printf("節(jié)點(diǎn)不存在\n");
}
}
主函數(shù)
int main()
{
TYPE* pt;//定義一個(gè)結(jié)構(gòu)體指針變量
pt = Creat();//函數(shù)返回鏈表第一個(gè)結(jié)點(diǎn)的地址
insert(pt, 1);//從1+1后插入
print(pt);//輸出調(diào)用
return 0;
}
結(jié)果顯示

出現(xiàn)的問題
1、出現(xiàn)scanf 和printf 在VS2019中使用時(shí)會出錯,解決辦法如下


到此這篇關(guān)于C語言鏈表詳解及代碼分析的文章就介紹到這了,更多相關(guān)C語言鏈表詳解內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C/C++使用socket實(shí)現(xiàn)判斷ip是否能連通
這篇文章主要為大家詳細(xì)介紹了C/C++如何使用socket實(shí)現(xiàn)判斷ip是否能連通,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解一下2023-07-07
C++的靜態(tài)聯(lián)編和動態(tài)聯(lián)編詳解
這篇文章主要介紹了C++的靜態(tài)聯(lián)編和動態(tài)聯(lián)編詳解,對于深入理解C++編譯運(yùn)行原理有很大幫助,需要的朋友可以參考下2014-07-07
vscode使用cmake時(shí)將命令行參數(shù)傳遞給調(diào)試目標(biāo)的方法
這篇文章主要介紹了vscode使用cmake時(shí)將命令行參數(shù)傳遞給調(diào)試目標(biāo),下面介紹了一個(gè)示例,將參數(shù)first_arg, second-arg和third arg傳遞給程序(此處需要注意,third arg中間雖然存在空格,但是仍然被視作一個(gè)參數(shù)),需要的朋友參考下吧2024-03-03
C++實(shí)例分析講解臨時(shí)對象與右值引用的用法
對性能來說,許多的問題都需要和出現(xiàn)頻率及本身執(zhí)行一次的開銷掛鉤,有些問題雖然看似比較開銷較大,但是很少會執(zhí)行到,那也不會對程序有大的影響;同樣一個(gè)很小開銷的函數(shù)執(zhí)行很頻繁,同樣會對程序的執(zhí)行效率有很大影響。本章中作者主要根據(jù)臨時(shí)對象來闡述這樣一個(gè)觀點(diǎn)2022-08-08
Qt+QWidget實(shí)現(xiàn)簡約美觀的加載動畫
這篇文章主要為大家詳細(xì)介紹了Qt如何結(jié)合QWidget實(shí)現(xiàn)簡約美觀的加載動畫,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-02-02

