版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
管態(tài)和目態(tài)
管態(tài)是指操作系統(tǒng)在運(yùn)行系統(tǒng)的管理程序時(shí)所處的狀態(tài)。此狀態(tài)下可以執(zhí)行任何指令,
包括特權(quán)指令。CPU的狀態(tài)屬于程序狀態(tài)字PSW的一位,管態(tài)又稱特權(quán)狀態(tài)、系統(tǒng)態(tài)或核
心態(tài)。通常,操作系統(tǒng)在管態(tài)下運(yùn)行,CPU在管態(tài)下可以執(zhí)行指令系統(tǒng)的全集。
目態(tài)是指操作系統(tǒng)在運(yùn)行系統(tǒng)的應(yīng)用程序所處的狀態(tài)。只允許應(yīng)用程序訪問自己的內(nèi)存空
間。這樣能夠保證應(yīng)用程序運(yùn)行時(shí)系統(tǒng)的安全。目態(tài)又稱常態(tài)或用戶態(tài),機(jī)器處于目態(tài)時(shí),
程序只能執(zhí)行非特權(quán)指令。用戶程序只能在目態(tài)下運(yùn)行。
當(dāng)一個(gè)任務(wù)(進(jìn)程)執(zhí)行系統(tǒng)調(diào)用而陷入內(nèi)核代碼中執(zhí)行時(shí),我們就稱進(jìn)程處于內(nèi)核運(yùn)
行態(tài)(或簡(jiǎn)稱為內(nèi)核態(tài))。此時(shí)處理器處于特權(quán)級(jí)最高的(0級(jí))內(nèi)核代碼中執(zhí)行。當(dāng)進(jìn)程
處于內(nèi)核態(tài)時(shí),執(zhí)行的內(nèi)核代碼會(huì)使用當(dāng)前進(jìn)程的內(nèi)核棧。每個(gè)進(jìn)程都有自己的內(nèi)核棧。當(dāng)
進(jìn)程在執(zhí)行用戶自己的代碼時(shí),則稱其處于用戶運(yùn)行態(tài)(用戶態(tài))。即此時(shí)處理器在特權(quán)級(jí)
最低的(3級(jí))用戶代碼中運(yùn)行。當(dāng)正在執(zhí)行用戶程序而突然被中斷程序中斷時(shí),此時(shí)用戶
程序也可以象征性地稱為處于進(jìn)程的內(nèi)核態(tài)。因?yàn)橹袛嗵幚沓绦驅(qū)⑹褂卯?dāng)前進(jìn)程的內(nèi)核棧。
這與處于內(nèi)核態(tài)的進(jìn)程的狀態(tài)有些類似。
1、用系統(tǒng)調(diào)用時(shí)進(jìn)入核心態(tài)。Linux對(duì)硬件的操作只能在核心態(tài),這可以通過寫驅(qū)動(dòng)
程序來控制。在用戶態(tài)操作硬件會(huì)造成coredump.
2、要注意區(qū)分系統(tǒng)調(diào)用和一般的函數(shù)。系統(tǒng)調(diào)用由內(nèi)核提供,如read。、write。、
open。等。而一般的函數(shù)山軟件包中的函數(shù)庫(kù)提供,如sin()、cos()等。在語法上兩者沒
有區(qū)別。
3、一般情況:系統(tǒng)調(diào)用運(yùn)行在核心態(tài),函數(shù)運(yùn)行在用戶態(tài)。但也有一些函數(shù)在內(nèi)部使
用了系統(tǒng)調(diào)用(如fopen),這樣的函數(shù)在調(diào)用系統(tǒng)調(diào)用是進(jìn)入核心態(tài),其他時(shí)候運(yùn)行在用
戶態(tài)。
大概是當(dāng)用戶程序調(diào)用系統(tǒng)的API時(shí),就產(chǎn)生中斷,進(jìn)入內(nèi)核態(tài)的API,處理完成后,
用中斷再退出,返回用戶態(tài)的調(diào)用函數(shù)。
userapi—>interrupt->kernelapi->interrupt
簡(jiǎn)單來講一個(gè)進(jìn)程由于執(zhí)行系統(tǒng)調(diào)用而開始執(zhí)行內(nèi)核代碼,我們稱該進(jìn)程處于內(nèi)核態(tài)中.
一個(gè)進(jìn)程執(zhí)行應(yīng)用程序自身代碼則稱該進(jìn)程處于用戶態(tài)。intelx86架構(gòu)的CPU分為好幾
個(gè)運(yùn)行級(jí)別,從0-3,0為最高級(jí)別,3為最低級(jí)別。
針對(duì)不同的級(jí)別,有很多的限制,比如說傳統(tǒng)的in,oul指令,就是端口的輸入輸出指
令,在0級(jí)下是可以用的,但在3級(jí)下就不能用,你用就產(chǎn)生陷阱,告訴你出錯(cuò)了,當(dāng)然限制還
有很多了,不只是這一點(diǎn)。
操作系統(tǒng)下是利用這個(gè)特點(diǎn),當(dāng)操作系統(tǒng)自己的代碼運(yùn)行時(shí),CPU就切成0級(jí),當(dāng)用戶的
程序運(yùn)行是就只讓它在3級(jí)運(yùn)行,這樣如果用戶的程序想做什么破壞系統(tǒng)的事情的話,也沒
辦法做到。
當(dāng)然,低級(jí)別的程序是沒法把自己升到高級(jí)別的,也就是說用戶程序運(yùn)行在3級(jí),他想把
自己變成0級(jí)自己是做不到的,除非是操作系統(tǒng)幫忙,利用這個(gè)特性,操作系統(tǒng)就可以控制
所有的程序的運(yùn)行,確保系統(tǒng)的安全了.平時(shí)把操作系統(tǒng)運(yùn)行時(shí)的級(jí)別就叫內(nèi)核態(tài)(因?yàn)槭?/p>
操作系統(tǒng)內(nèi)核運(yùn)行時(shí)的狀態(tài)),而且普通用戶程序運(yùn)行時(shí)的那個(gè)級(jí)別叫用戶態(tài)...
當(dāng)操作系統(tǒng)剛引導(dǎo)時(shí),CPU處于實(shí)模式,這時(shí)就相當(dāng)于是0級(jí),于是操作系統(tǒng)就自動(dòng)得到
最高權(quán)限,然后切到保護(hù)模式時(shí)就是0級(jí),這時(shí)操作系統(tǒng)就占了先機(jī),成為了最高級(jí)別的運(yùn)行
1
者?,由于你的程序都是由操作系統(tǒng)來加載的,所以當(dāng)它把你加載上來后,就把你的運(yùn)行狀態(tài)設(shè)
為3級(jí),即最低級(jí),然后才讓你運(yùn)行,所以沒辦法,你只能在最低級(jí)運(yùn)行了,因?yàn)闆]辦法把自
己從低級(jí)上升到高級(jí),這就是操作系統(tǒng)在內(nèi)核態(tài)可以管理用戶程序,殺死用戶程序的原
因.
特權(quán)指令
所謂特權(quán)指令是指具有特殊權(quán)限的指令,由于這類指令的權(quán)限最大,所以如果使用不當(dāng),
就會(huì)破壞系統(tǒng)或其它用戶信.息.因此為了安全起見,這類指令只能用于操作系統(tǒng)或其它系統(tǒng)
軟件,而一般不直接提供給用戶使用。
這得從CPU指令系統(tǒng)(用于控制CPU完成各種功能的命令)的特權(quán)級(jí)別說起。在CPU的所
有指令中,有一些指令是非常危險(xiǎn)的,如果錯(cuò)用,將導(dǎo)致整個(gè)系統(tǒng)崩潰。比如:清內(nèi)存、設(shè)
置時(shí)鐘等。如果所有的程序都能使用這些指令,那么你的系統(tǒng)一天死機(jī)n回就不足為奇了。
所以,CPU將指令分為特權(quán)指令和非特權(quán)指令,對(duì)于那些危險(xiǎn)的指令,只允許操作系統(tǒng)及其
相關(guān)模塊使用,普通的應(yīng)用程序只能使用那些不會(huì)造成災(zāi)難的指令。形象地說,特權(quán)指令就
是那些兒童不宜的東東,而非特權(quán)指令則是老少皆宜。
特權(quán)指令如果錯(cuò)用,將導(dǎo)致整個(gè)系統(tǒng)崩潰。比如:清內(nèi)存、設(shè)置時(shí)鐘等。如果所有的程
序都能使用這些指令,那么你的系統(tǒng)-天死機(jī)n回就不足為奇了。
一般說來,在單用戶,單任務(wù)的計(jì)算機(jī)中不具有也不需要特權(quán)指令,而在多用戶,多任
務(wù)的計(jì)算機(jī)系統(tǒng)中,特權(quán)指令卻是不可缺少的。它主要用于系統(tǒng)資源的分配和管理,包括改
變系統(tǒng)的工作方式,檢測(cè)用戶的訪問權(quán)限,修改虛擬存儲(chǔ)器管理的段表,頁(yè)表和完成任務(wù)的
創(chuàng)建和切換等。
系統(tǒng)調(diào)用命令
系統(tǒng)調(diào)用是用戶程序請(qǐng)求操作系統(tǒng)為其服務(wù)的惟一形式,在UNIX中把系統(tǒng)調(diào)用稱為程
序員接口。UNIX規(guī)定用戶程序用捕俘(trap)指令請(qǐng)求系統(tǒng)服務(wù),UNIX核心中的中斷捕俘程
序根據(jù)trap的類型轉(zhuǎn)向相應(yīng)的處理程序
訪管指令
訪管指令是一條可以在目態(tài)下執(zhí)行的指令,用戶程序中凡是要調(diào)用操作系統(tǒng)功能時(shí)就安
排一條訪管指令。當(dāng)處理器執(zhí)行到訪管指令時(shí)就產(chǎn)生一個(gè)中斷事件(自愿中斷),暫停用戶
程序的執(zhí)行,而讓操作系統(tǒng)來為用戶服務(wù)
廣義指令
廣義指令是指VC或者其他一些編程環(huán)境提供的庫(kù)函數(shù),這些庫(kù)函數(shù)集成了操作系統(tǒng)提
供的系統(tǒng)調(diào)用,比如API
2
雙向鏈表
雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個(gè)數(shù)據(jù)結(jié)點(diǎn)中都有兩個(gè)指針,分別指向直
接后繼和直接前驅(qū)。所以,從雙向鏈表中的任意一個(gè)結(jié)點(diǎn)開始,都可以很方便地訪問它的前
驅(qū)結(jié)點(diǎn)和后繼結(jié)點(diǎn)。一般我們都構(gòu)造雙向循環(huán)鏈表。
鏈表的操作
線性表的雙向鏈表存儲(chǔ)結(jié)構(gòu)
typedefstructDuLNode
(
ElemTypedata;
structDuLNode*prior,*next;
}DuLNode,*DuLinkList;
帶頭結(jié)點(diǎn)的雙向循環(huán)鏈表的基本操作
voidInitList(DuLinkList*L)
{/*產(chǎn)生空的雙向循環(huán)鏈表L*/
*L=(DuLinkList)maHoc(sizeof(DuLNode));
if(*L)
(*L)->next=(*L)->prior=*L;
else
exit(OVERFLOW);
)
銷毀雙向循環(huán)鏈表L
voidDestroyList(DuLinkList*L)
(
DuLinkListq,p=(*L)->next;/*p指向第一個(gè)結(jié)點(diǎn)*/
while(p!=*L)/*p沒到表頭*/
(
q=p->next;
free(p);
P=q;
)
3
free(*L);
*L=NULL;
)
重置鏈表為空表
voidClearList(DuLinkListL)/*不改變L*/
{DuLinkListq,p=L->next;/*p指向第一個(gè)結(jié)點(diǎn)*/
while(p!=L)/*p沒到表頭*/
(
q=p->next;
free(p);
P二q;
)
L->next=L->prior=L;/*頭結(jié)點(diǎn)的兩個(gè)指針域均指向自身*/
)
驗(yàn)證是否為空表
StatusListEmpty(DuLinkListL)
{/*初始條件:線性表L已存在
if(L->next==L&&L->prior==L)
returnTRUE;
else
returnFALSE;
)
元素的操作
計(jì)算表內(nèi)元素個(gè)數(shù)
intListLength(DuLinkListL)
{/*初始條件:L已存在。操作結(jié)果:*/
inti=0;
DuLinkListp=L->next;/*p指向第一個(gè)結(jié)點(diǎn)*/
while(p!=L)/*p沒到表頭*/
4
i++;
p=p->next;
)
returni;
)
賦值
StatusGetElem(DuLinkListL,inti,ElemType*e)
{/*當(dāng)?shù)趇個(gè)元素存在時(shí),其值賦給e并返回OK,否則返回ERROR*/
intj=l;/*j為計(jì)數(shù)器*/
DuLinkListp=L->next;/*p指向第一個(gè)結(jié)點(diǎn)*/
while(p!=L&,&j<i)
(
p=p->next;
j++;
)
if(p==L||j>i)/*第i個(gè)元素不存在*/
returnERROR;
*e=p->data;/*取第i個(gè)元素*/
returnOK;
)
查找元素
intLocateElem(DuLinkListL,ElemTypee,Status(*compare)(ElemType,ElemType))
{/*初始條件:L已存在,compare。是數(shù)據(jù)元素判定函數(shù)*/
/*操作結(jié)果:返回L中第1個(gè)與e滿足關(guān)系compare。的數(shù)據(jù)元素的位序。*/
/*若這樣的數(shù)據(jù)元素不存在,則返回值為0*/
inti=0;
DuLinkListp=L->next;/*p指向第1個(gè)元素*/
while(p!=L)
5
i++;
if(compare(p->data,e))/*找到這樣的數(shù)據(jù)元素*/
returni;
p=p->next;
)
return0;
)
查找元素前驅(qū)
StatusPriorElem(DuLinkListL,ElemTypecure,ElemType*pree)
{/*操作結(jié)果:若cur_e是L的數(shù)據(jù)元素,且不是第一個(gè),則用pre_e返回它
的前驅(qū)*/
/*否則操作失敗,pre_e無定義*/
DuLinkListp=L->next->next;/*p指向第2個(gè)元素*/
while(p!=L)/*p沒到表頭*/
(
if(p->data==cur_e)
(
*pree=p->prior->data;
returnTRUE;
)
p=p->next;
)
returnFALSE;
)
查找元素后繼
StatusNextElem(DuLinkListL,ElemTypecur_e,ElemType*next_e)
{/*操作結(jié)果:若cur_e是L的數(shù)據(jù)元素,且不是最后一個(gè),則用next_e返回
它的后繼,*/
/*否則操作失敗,next_e無定義*/
6
DuLinkListp=L->next->next;/*p指向第2個(gè)元素*/
while(p!=L)/*p沒到表頭*/
{
if(p->prior->data-cur_e)
(
*next_e=p->data;
returnTRUE;
)
p=p->next;
)
returnFALSE;
)
查找元素地址
DuLinkListGetElemP(DuLinkListL,inti)/*另加*/
{/*在雙向鏈表L中返回第i個(gè)元素的地址。i為0,返回頭結(jié)點(diǎn)的地址。若第
i個(gè)元素不存在,*/
/*返回NULL*/
intj;
DuLinkListp=L;/*p指向頭結(jié)點(diǎn)*/
if(i<0||i>ListLength(D)/*i值不合法*/
returnNULL;
for(j=l;j<=i;j++)
p=p->next;
returnp;
)
元素的插入
StatusListinsert(DuLinkListL,inti,ElemTypee)
{/*在帶頭結(jié)點(diǎn)的雙鏈循環(huán)線性表L中第i個(gè)位置之前插入元素e,i的合法值
為IWiW表長(zhǎng)+1*/
/*改進(jìn)算法2.18,否則無法在第表長(zhǎng)+1個(gè)結(jié)點(diǎn)之前插入元素*/
7
DuLinkListp,s;
if(i<l||i>ListLength(L)+l)/*i值不合法*/
returnERROR;
p=GetElemP(L,i-1);/*在L中確定第i個(gè)元素前驅(qū)的位置指針p*/
if(!p)/*p-NULL,即第i個(gè)元素的前驅(qū)不存在(設(shè)頭結(jié)點(diǎn)為第1個(gè)元素的前驅(qū))*/
returnERROR;
s=(DuLinkList)malloc(sizeof(DuLNode));
if(!s)
returnOVERFLOW;
s->data=e;
s->prior=p;/*在第iT個(gè)元素之后插入*/
s->next=p->next;
p->next->prior=s;
p->next=s;
returnOK;
)
元素的刪除
StatusListDelete(DuLinkListL,inti,ElemType*e)
{/*刪除帶頭結(jié)點(diǎn)的雙鏈循環(huán)線性表L的第i個(gè)元素,i的合法值為IWiW表
長(zhǎng)*/
DuLinkListp;
if(i<l)/*i值不合法*/
returnERROR;
p=GetElemP(L,i);/*在L中確定第i個(gè)元素的位置指針p*/
if(!p)/*p=NULL,即第i個(gè)元素不存在*/
returnERROR;
*e=p->data;
p->prior->next=p->next;
p->next->prior=p->prior;
8
free(p);
returnOK;
)
正序查找
voidListTraverse(DuLinkListL,void(*visit)(ElemType))
{/*由雙鏈循環(huán)線性表L的頭結(jié)點(diǎn)出發(fā),正序?qū)γ總€(gè)數(shù)據(jù)元素調(diào)用函數(shù)visit。
*/
DuLinkListp=L->next;/*p指向頭結(jié)點(diǎn)*/
while(p!=L)
(
visit(p->data);
p=p->next;
)
printf(*\n*);
)
voidListTraverseBack(DuLinkListL,void(*visit)(ElemType))
逆序查找
{/*由雙鏈循環(huán)線性表L的頭結(jié)點(diǎn)出發(fā),逆序?qū)γ總€(gè)數(shù)據(jù)元素調(diào)用函數(shù)visitOo
另加*/
DuLinkListp=L->prior;/*p指向尾結(jié)點(diǎn)*/
while(p!=L)
(
visit(p->data);
p=p->prior;
)
printf("\n");
)
雙向鏈表模板
/*文件名:LinkedList.h
*功能:實(shí)現(xiàn)雙向鏈表的基本功能
9
*注意:為了使最終程序執(zhí)行得更快,僅在Debug模式下檢測(cè)操作合法性。
*另外不對(duì)內(nèi)存分配失敗作處理,因一般情況下應(yīng)用程序有近2GB真正可用的空間*/
^pragmaonce
iiinclude<assert.h>
template<classT>
classLinkedList
(
private:
classNode
(
public:
Tdata;〃數(shù)據(jù)域,不要求泛型T的實(shí)例類有無參構(gòu)造函數(shù)
Node*prior;//指向前一個(gè)結(jié)點(diǎn)
Node*next;//指向下一個(gè)結(jié)點(diǎn)
Node(constT&element,Node*&pri,Node*&nt):data(element),next(nt),
prior(pri){}
Node0:data(data){}〃泛型T的實(shí)例類的復(fù)制構(gòu)造函數(shù)將被調(diào)用.在Vc2010測(cè)
試可行
);
Node*head;〃指向第一個(gè)結(jié)點(diǎn)
public:
〃初始化:構(gòu)造一個(gè)空結(jié)點(diǎn),搭建空鏈
LinkedList():head(newNode()){head->prior=head->next=head;}
〃獲取元素總數(shù)
intelementToatal()const;
〃判斷是否為空鏈
boolisEmpty()const{returnhead==head->next?true:false;}
〃將元素添加至最后,注意node的指針設(shè)置
voidaddToLast(constT&element){
Node*ne=newNode(element,head->prior,head);
10
head->prior=head->prior->next=ne;}
〃獲取最后一個(gè)元素
TgetLastElement()const{assert(!isEmpty());returnhead->prior->data;}
〃刪除最后一個(gè)元素,注意node的指針設(shè)置
voiddelLastElement(){
assert(!isEmpty());
Node*p=head->prior->prior;
deletehead->prior;head->prior二p;p->next=head;
)
〃修改最后一個(gè)元素
voidalterLastEmlent(constT&newElement){
assert(!isEmpty());
head->prior->data=newElement;
)
〃插入元素
voidinsertElement(constT&element,intposition);
〃獲取元素
TgetElement(intindex)const;
〃刪除元素
TdelElement(intindex);
〃修改元素
voidalterElement(constT&Newelement,intindex);
〃查找元素
intfindElement(constT&element)const;
〃正序遍歷
voidTraverse(void(*visit)(T&element));
〃逆序遍歷
voidTraverseBack(void(*visit)(T&element));
〃重載□函數(shù)
11
T&operator[](intindex);
〃清空鏈表
voidclearAHElement();
〃銷毀鏈表
^LinkedList();
);
/*返回元素總數(shù)*/
template<classT>
intLinkedList<T>::elementToatal()const
(
intTotal=0;
for(Node*p=head->next;p!=head;p=p->next)++Total;
returnTotal;
)
/*在position指定的位置插入元素。原來position及后面的元素后移*/
template<classT>
voidLinkedList<T>::insertElement(constT&element,intposition)
(
assert(position>0&,&position<=elementToatal()+1);
Node*p=head;
while(position)
(
p=p->next;
position一-;
)
//此時(shí)p指向要插入的結(jié)點(diǎn)
Node*pNew=newNode(element,p->prior,p);
p->priorz:p->prior->next=pNew;
)
12
/*返回找到的元素的副本*/
template<classT>
TLinkedList<T>::getElement(intindex)const
(
assert(index>0&&index<=elementToatal()&&!isEmpty());〃位置索引是否
合法,鏈表是否空
Node*p=head->next;
whi1e(--index)p=p->next;
returnp->data;
)
/*刪除指定元素,并返回它*/
template<classT>
TLinkedList<T>::delElement(intindex)
(
assert(index>0&&index<=elementToatal()&&!isEmpty());//位置索引是否
合法,鏈表是否空
Node*del=head->next;
whi1e(―index)del=del->next;
〃此時(shí)p指向要?jiǎng)h除元素
del->prior->next=del->next;
del->next->prior=del->prior;
TdelData=del->data;
deletedel;
returndelData;
)
/*用Newelement代替索引為index的元素*/
template<classT>
voidLinkedList<T>::alterElement(constT&Newelement,intindex)
(
assert(index>0&&index<=elementToatal()&&!isEmpty());〃位置索引是否
13
合法,鏈表是否空
Node*p=head->next;
whi1e(--index)p=p->next;
p->data=Newelement;
)
/*找到返回元素的索引,否則返回o*/
template<classT>
intLinkedList<T>::findElement(constT&element)const
(
Node*p=head->next;
inti=0;
while(p!=head)
(
i++;
if(p->data==element)returni;
p=p->next;
)
return0;
)
/*正向遍歷,以鏈表中每個(gè)元素作為參數(shù)調(diào)用visit函數(shù)*/
template<classT>
voidLinkedList<T>::Traverse(void(*visit)(T&element))
(
Node*p=head-〉next;
while(p!=head)
(
visit(p->data);〃注意此時(shí)外部visit函數(shù)有權(quán)限修改LinkedList〈T》的私有
數(shù)據(jù)
p=p->next;
14
}}
/*反向遍歷,以鏈表中每個(gè)元素作為參數(shù)調(diào)用Visit函數(shù)*/
template<classT>
voidLinkedList<T>::TraverseBack(void(*visit)(T&element))
(
Node*p=head->prior;
while(p!=head)
(
visit(p->data);〃注意此時(shí)外部visit函數(shù)有權(quán)限修改LinkedList<T>的私有
數(shù)據(jù)
p=p->prior;
}}
/*返回鏈表的元素引用,并可讀寫.實(shí)際上鏈表沒有口意義上的所有功能。因此口
函數(shù)是有限制的.重載它是為了客戶端代碼簡(jiǎn)潔,因?yàn)閺逆湵碜x寫數(shù)據(jù)是最常用的*/
template<classT>
T&LinkedList<T>::operator[](intindex)
(
assert(index>0&&index<=elementToatal()&&!isEmpty());〃□函數(shù)使用前
提條件
Node*p=head->next;
whi1e(--index)p=p->next;
returnp->data;
)
/*清空鏈表*/
template<classT>
voidLinkedList<T>::clearAllElement()
{
Node*p=head->next,*pTemp=0;
while(p!=head)
15
pTemp=p->next;
deletep;
p=pTemp;
)
head->priorz:head->next=head;〃收尾工作
)
/*析構(gòu)函數(shù),若內(nèi)存足夠沒必要調(diào)用該函數(shù)*/
template<classT>
LinkedList<T>::^LinkedList()
(
if(head)〃防止用戶顯式析構(gòu)后,對(duì)象又剛好超出作用域再調(diào)用該函數(shù)
(
clearAllElement();
deletehead;
head=O;
})
循環(huán)鏈表
循環(huán)鏈表是另?種形式的鏈?zhǔn)酱尜A結(jié)構(gòu)。它的特點(diǎn)是表中最后個(gè)結(jié)點(diǎn)的指針域指向頭
結(jié)點(diǎn),整個(gè)鏈表形成一個(gè)環(huán)。
分類:
(1)單循環(huán)鏈表一一在單鏈表中,將終端結(jié)點(diǎn)的指針域NULL改為指向表頭結(jié)點(diǎn)
或開始結(jié)點(diǎn)即可。
(2)多重鏈的循環(huán)鏈表一一將表中結(jié)點(diǎn)鏈在多個(gè)環(huán)上。
帶頭結(jié)點(diǎn)的單循環(huán)鏈表:判斷空鏈表的條件是head-head->next;
僅設(shè)尾指針的單循環(huán)鏈表:用尾指針rear表示的單循環(huán)鏈表對(duì)開始結(jié)點(diǎn)al和終端結(jié)
點(diǎn)an查找時(shí)間都是0(1)。而表的操作常常是在表的首尾位置上進(jìn)行,因此,實(shí)用中
多采用尾指針表示單循環(huán)鏈表。帶尾指針的單循環(huán)鏈表可見下圖。
注意:判斷空鏈表的條件為rear==rear->next;
循環(huán)鏈表的特點(diǎn):循環(huán)鏈表的特點(diǎn)是無須增加存儲(chǔ)量,僅對(duì)表的鏈接方式稍作改變,
即可使得表處理更加方便靈活。
【例】在鏈表上實(shí)現(xiàn)將兩個(gè)線性表(al,a2,…,an)和(bl,b2,…,bm)連
16
接成一個(gè)線性表(al,…,an,bl,?-?bm)的運(yùn)算。
分析:若在單鏈表或頭指針表示的單循環(huán)表上做這種鏈接操作,都需要遍歷第一
個(gè)鏈表,找到結(jié)點(diǎn)an,然后將結(jié)點(diǎn)bl鏈到an的后面,其執(zhí)行時(shí)間是0(n)。若在尾
指針表示的單循環(huán)鏈表上實(shí)現(xiàn),則只需修改指針,無須遍歷,其執(zhí)行時(shí)間是0(1)。
相應(yīng)的算法如下:
LinkListConnect(LinkListA,LinkListB)
{〃假設(shè)A,B為非空循環(huán)鏈表的尾指針
LinkListp=A->next;//①保存A表的頭結(jié)點(diǎn)位置
A->next=B->next->next;〃②B表的開始結(jié)點(diǎn)鏈接到A表尾
free(B->next);〃③釋放B表的頭結(jié)點(diǎn)
B->next=p;//@
returnB;〃返回新循環(huán)鏈表的尾指針
)
注意:
①循環(huán)鏈表中沒有NULL指針。涉及遍歷操作時(shí),其終止條件就不再是像非循環(huán)
鏈表那樣判別P或p->next是否為空,而是判別它們是否等于某一指定指針,如頭
指針或尾指針等。
②在單鏈表中,從一已知結(jié)點(diǎn)出發(fā),只能訪問到該結(jié)點(diǎn)及其后續(xù)結(jié)點(diǎn),無法找到
該結(jié)點(diǎn)之前的其它結(jié)點(diǎn)。而在單循環(huán)鏈表中,從任一結(jié)點(diǎn)出發(fā)都可訪問到表中所有結(jié)
點(diǎn),這一優(yōu)點(diǎn)使某些運(yùn)算在單循環(huán)鏈表上易于實(shí)現(xiàn)。
單鏈表
單鏈表簡(jiǎn)介:用組地址任意的存儲(chǔ)單元存放線性表中的數(shù)據(jù)元素。
以元素(數(shù)據(jù)元素的映象)+指針(指示后繼元素存儲(chǔ)位置)=結(jié)點(diǎn)(表示數(shù)據(jù)元
素或數(shù)據(jù)元素的映象)
以“結(jié)點(diǎn)的序列”表示線性表??稱作線性鏈表(單鏈表)
循環(huán)單鏈表是單鏈表的另一種形式,其結(jié)構(gòu)特點(diǎn)鏈表中最后一個(gè)結(jié)點(diǎn)的指針域不
再是結(jié)束標(biāo)記,而是指向整個(gè)鏈表的第一個(gè)結(jié)點(diǎn),從而使鏈表形成一個(gè)環(huán)。和單鏈表
相同,循環(huán)鏈表也有帶頭結(jié)點(diǎn)結(jié)構(gòu)和不帶頭結(jié)點(diǎn)結(jié)構(gòu)兩種,帶頭結(jié)點(diǎn)的循環(huán)單鏈表實(shí)
現(xiàn)插入和刪除操作較為方便。
單鏈表是一種順序存取的結(jié)構(gòu),為找第i個(gè)數(shù)據(jù)元素,必須先找到第iT個(gè)數(shù)據(jù)
元素。
因此,查找第i個(gè)數(shù)據(jù)元素的基本操作為:移動(dòng)指針,比較j和i
單鏈表
17
1、鏈接存儲(chǔ)方法
鏈接方式存儲(chǔ)的線性表簡(jiǎn)稱為鏈表(LinkedList)?
鏈表的具體存儲(chǔ)表示為:
①用一組任意的存儲(chǔ)單元來存放線性表的結(jié)點(diǎn)(這組存儲(chǔ)單元既可以是連續(xù)的,
也可以是不連續(xù)的)
②鏈表中結(jié)點(diǎn)的邏輯次序和物理次序不一定相同。為了能正確表示結(jié)點(diǎn)間的邏
輯關(guān)系,在存儲(chǔ)每個(gè)結(jié)點(diǎn)值的同時(shí),還必須存儲(chǔ)指示其后繼結(jié)點(diǎn)的地址(或位置)信
息(稱為指針(pointer)或鏈(link))
注意:
鏈?zhǔn)酱鎯?chǔ)是最常用的存儲(chǔ)方式之一,它不僅可用來表示線性表,而且可用來表示
各種非線性的數(shù)據(jù)結(jié)構(gòu)。
2、鏈表的結(jié)點(diǎn)結(jié)構(gòu)
I------1-------1
|data|next!
I______I_______l
data域一存放結(jié)點(diǎn)值的數(shù)據(jù)域
next域一存放結(jié)點(diǎn)的直接后繼的地址(位置)的指針域(鏈域)
注意:
①鏈表通過每個(gè)結(jié)點(diǎn)的鏈域?qū)⒕€性表的n個(gè)結(jié)點(diǎn)按其邏輯順序鏈接在一起的。
②每個(gè)結(jié)點(diǎn)只有一個(gè)鏈域的鏈表稱為單鏈表(SingleLinkedList)。
【例】線性表(bat,cat,eat,fat,hat,jat,lat,mat)的單鏈表示如示意
圖
3、頭指針head和終端結(jié)點(diǎn)指針域的表示
單鏈表中每個(gè)結(jié)點(diǎn)的存儲(chǔ)地址是存放在其前趨結(jié)點(diǎn)next域中,而開始結(jié)點(diǎn)無前
趨,故應(yīng)設(shè)頭指針head指向開始結(jié)點(diǎn)。
注意:
鏈表由頭指針唯一確定,單鏈表可以用頭指針的名字來命名。
【例】頭指針名是head的鏈表可稱為表heado
終端結(jié)點(diǎn)無后繼,故終端結(jié)點(diǎn)的指針域?yàn)榭眨碞ULL。
4、單鏈表的一般圖示法
由于我們常常只注重結(jié)點(diǎn)間的邏輯順序,不關(guān)心每個(gè)結(jié)點(diǎn)的實(shí)際位置,可以用箭
18
頭來表示鏈域中的指針,線性表(bat,cat,fat,hat,jat,lat,mat)的單鏈表
就可以表示為下圖形式。
5、單鏈表類型描述
typedefcharDataType;//假設(shè)結(jié)點(diǎn)的數(shù)據(jù)域類型為字符
typedefstructnode{〃結(jié)點(diǎn)類型定義
DataTypedata;〃結(jié)點(diǎn)的數(shù)據(jù)域
structnode*next;〃結(jié)點(diǎn)的指針域
}ListNode;
typedefListNode*LinkList;
ListNode*p;
LinkListhead;
注意:
①*LinkList和ListNode是不同名字的同一個(gè)指針類型(命名的不同是為了概念
匕更明確)
②*LinkList類型的指針變量head表示它是單鏈表的頭指針
③ListNode類型的指針變量p表示它是指向某一結(jié)點(diǎn)的指針
6、指針變量和結(jié)點(diǎn)變量
指針變量結(jié)點(diǎn)變量
定義在變量說明部分顯式定義在程序執(zhí)行時(shí),通過標(biāo)準(zhǔn)函數(shù)malloc生成
取值非空時(shí),存放某類型結(jié)點(diǎn)實(shí)際存放結(jié)點(diǎn)各域內(nèi)容的地址
操作方式通過指針變量名訪問通過指針生成、訪問和釋放
①生成結(jié)點(diǎn)變量的標(biāo)準(zhǔn)函數(shù)
p=(ListNode*)malloc(sizeof(ListNode));
〃函數(shù)malloc分配一個(gè)類型為L(zhǎng)istNode的結(jié)點(diǎn)變量的空間,并將其首地址放入
指針變量P中
②釋放結(jié)點(diǎn)變量空間的標(biāo)準(zhǔn)函數(shù)
free(p);〃釋放p所指的結(jié)點(diǎn)變量空間
③結(jié)點(diǎn)分量的訪問
利用結(jié)點(diǎn)變量的名字*P訪問結(jié)點(diǎn)分量
方法一:(*p).data和(*p).next
19
方法二:p->data和p->next
④指針變量P和結(jié)點(diǎn)變量*P的關(guān)系
指針變量P的值一一結(jié)點(diǎn)地址
結(jié)點(diǎn)變量*P的值一一結(jié)點(diǎn)內(nèi)容
(*p).data的值一一p指針?biāo)附Y(jié)點(diǎn)的data域的值
(*p).next的值----*p后繼結(jié)點(diǎn)的地址
*((*p).next)------*p后繼結(jié)點(diǎn)
注意:
①若指針變量P的值為空(NULL),則它不指向任何結(jié)點(diǎn)。此時(shí),若通過*p來
訪問結(jié)點(diǎn)就意味著訪問?個(gè)不存在的變量,從而引起程序的錯(cuò)誤。
②有關(guān)指針類型的意義和說明方式的詳細(xì)解釋
可見,在鏈表中插入結(jié)點(diǎn)只需要修改指針。但同時(shí),若要在第i個(gè)結(jié)點(diǎn)之前插
入元素,修改的是第『1個(gè)結(jié)點(diǎn)的指針。
因此,在單鏈表中第i個(gè)結(jié)點(diǎn)之前進(jìn)行插入的基本操作為:
找到線性表中第i-1個(gè)結(jié)點(diǎn),然后修改其指向后繼的指針。
單鏈表的建立
鏈表操作中動(dòng)態(tài)存儲(chǔ)分配要使用標(biāo)準(zhǔn)函數(shù),先介紹一下這些函數(shù)。
(l)malloc(size)
在內(nèi)存的動(dòng)態(tài)存儲(chǔ)區(qū)申請(qǐng)一個(gè)長(zhǎng)度為size字節(jié)的連續(xù)空間。
(2)calloc(n,size)
在內(nèi)存的動(dòng)態(tài)存儲(chǔ)區(qū)申請(qǐng)n個(gè)長(zhǎng)度為size字節(jié)的連續(xù)空間,函數(shù)返回值為分配
空間的首地址。若此函數(shù)未被成功執(zhí)行,函數(shù)返回值為。。
(3)free(p)
釋放由指針P所指向的存儲(chǔ)單元,而存儲(chǔ)單元的大小是最近一次調(diào)用mallocO
或calloc()函數(shù)時(shí)所申請(qǐng)的存儲(chǔ)空間。
在頭文件V'stdlib.h”中包含了這些函數(shù)的信息,使用這些函數(shù)時(shí)需在程序開
頭用文件包含指令#include"stdlib.h”指明。
另請(qǐng)讀者注意,調(diào)用動(dòng)態(tài)存儲(chǔ)分配函數(shù)返回的指針是指向void類型或char類型
的指針,在具體使用時(shí),要根據(jù)所指向的數(shù)據(jù)進(jìn)行強(qiáng)制類型轉(zhuǎn)換。
單鏈表的建立有頭插法、尾插法兩種方法。
1.頭插法
20
單鏈表是用戶不斷申請(qǐng)存儲(chǔ)單元和改變鏈接關(guān)系而得到的一種特殊數(shù)據(jù)結(jié)構(gòu),將
鏈表的左邊稱為鏈頭,右邊稱為鏈尾。頭插法建單鏈表是將鏈表右端看成固定的,鏈
表不斷向左延伸而得到的。頭插法最先得到的是尾結(jié)點(diǎn)。
由于鏈表的長(zhǎng)度是隨機(jī)的,故用一個(gè)while循環(huán)來控制鏈表中結(jié)點(diǎn)個(gè)數(shù)。假設(shè)每
個(gè)結(jié)點(diǎn)的值都大于0,則循環(huán)條件為輸入的值大于。。申請(qǐng)存儲(chǔ)空間可使用mallocO
函數(shù)實(shí)現(xiàn),需設(shè)立-申請(qǐng)單元指針,但mallocO函數(shù)得到的指針并不是指向結(jié)構(gòu)體的
指針,需使用強(qiáng)制類型轉(zhuǎn)換,將其轉(zhuǎn)換成結(jié)構(gòu)體型指針。剛開始時(shí),鏈表還沒建立,
是一空鏈表,head指針為NULL。
鏈表建立的過程是申請(qǐng)空間、得到數(shù)據(jù)、建立鏈接的循環(huán)處理過程。
2.尾插法
若將鏈表的左端固定,鏈表不斷向右延伸,這種建立鏈表的方法稱為尾插法。
尾插法建立鏈表時(shí),頭指針固定不動(dòng),故必須設(shè)立一個(gè)搜索指針,向鏈表右邊延伸,
則整個(gè)算法中應(yīng)設(shè)立三個(gè)鏈表指針,即頭指針head、搜索指針p2、申請(qǐng)單元指針pl,
尾插法最先得到的是頭結(jié)點(diǎn)。
單鏈表c語言表示:
#include<stdio.h>
#include<stdlib.h>
structlinknode〃建立鏈表節(jié)點(diǎn)
(
intdata;〃需要更通用的數(shù)據(jù)類型
structlinknode*next;
);
structlink
(
structlinknode*head;
structlinknode*tail;
);
structlinknode*create()〃創(chuàng)建鏈表,接受INT型值
intdatas;
structlinknode*head,*temp,*tai1;
head=tail=NULL;
21
while(scanf(〃%d",&datas)==l)〃輸入方式有待改進(jìn)
temp=(structlinknode*)maHoc(sizeof(structlinknode));
if(temp二二NULL)
printf(''allocateerro!z,);
else
(
temp->data=datas;
temp->next=NULL;
if(head二二NULL)
head=tai1=temp;
else
(
tail->next=temp;
tail=temp;
}})
returnhead;
)
voidprint(structlinknode*head)〃打印鏈表
(
structlinknode*p;
p=head;
while(p)
(
printf(,,%d\tz,,p->data);
p=p->next;
}}
structlinknode*find(structlinknode*head,intdatas)〃查找特定的值
的節(jié)點(diǎn)
22
structlinknode*p;
p=head;
while(p->data!=datas&&p->next!=NULL)
(
p=p->next;
)
if(p->data==datas)
returnp;
else
returnNULL;
)
structlinknode*findAhead(structlinknode*head,intdatas)〃查找特定
值得前一個(gè)節(jié)點(diǎn)
(
structlinknode*p,*q;
q=NULL;
p=head;
while(p->data!=datas&&p->next!=NULL)
(
Q=P;
p=p->next;
)
if(p->data==datas)
returnq;
else
returnNULL;
)
structlinknode*enterTohead(structlinknode*head,intdatas)〃在頭部
添加節(jié)點(diǎn)
23
{〃改變了頭節(jié)點(diǎn)指針,需重新賦值
structlinknode*enter;
enter=(structlinknode*)malloc(sizeof(struct1inknode));
if(enter==NULL)
printf(''allocateerro!z,);
enter->data=datas;
enter->next=NULL;
if(head二二NULL)
head=enter;
else
(
enter->next=head;
head=enter;
)
returnhead;
)
structlinknode*enter
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2022年高中工作計(jì)劃
- 2025年柔性自動(dòng)化裝備項(xiàng)目合作計(jì)劃書
- 2025年耐高溫濾料合作協(xié)議書
- 入職競(jìng)業(yè)協(xié)議書(2篇)
- 2025年高純石英纖維正交三向織物項(xiàng)目發(fā)展計(jì)劃
- 2025年青霉素類抗菌藥物合作協(xié)議書
- 地下車庫(kù)租賃協(xié)議
- 肉雞養(yǎng)殖場(chǎng)租賃合同
- 股票期權(quán)贈(zèng)與合同
- 廠房出租居間合同
- 農(nóng)業(yè)農(nóng)村基礎(chǔ)知識(shí)考試復(fù)習(xí)題庫(kù)寶典(600多題)
- 綠色港口等級(jí)評(píng)價(jià)指南
- “非遺”之首-昆曲經(jīng)典藝術(shù)欣賞智慧樹知到期末考試答案章節(jié)答案2024年北京大學(xué)
- DL∕T 1704-2017 脫硫濕磨機(jī)石灰石制漿系統(tǒng)性能測(cè)試方法
- SMP-04-013-00 藥品受托企業(yè)審計(jì)評(píng)估管理規(guī)程
- 高二物理寒假作業(yè)
- 2024中考英語試題研究《漢堡包寫作法助力中考英語書面表達(dá)》課件
- 建筑公司績(jī)效考核辦法1
- 服務(wù)質(zhì)量保障措施及進(jìn)度保障措施
- 燃?xì)廨斉湫袠I(yè)經(jīng)營(yíng)分析報(bào)告
- 2024年高考?xì)v史試卷(浙江)(1月)(解析卷)
評(píng)論
0/150
提交評(píng)論