您好,登錄后才能下訂單哦!
指針原理:
指針就是指向一個(gè)特定內(nèi)存地址的一個(gè)變量。簡(jiǎn)化了的內(nèi)存空間模型是按照從0到某一個(gè)數(shù)(比如1048575=1M-1)的一維線性空間,其中的每一個(gè)數(shù)對(duì)應(yīng)一個(gè)存儲(chǔ)單元,即1個(gè)字節(jié)。指針有兩個(gè)屬性:指向性和偏移性。指向性指的是指針一定要有一個(gè)確定的指向,偏移性則是體現(xiàn)指針重要應(yīng)用的方面,即指針可以按程序員的要求向前或向后偏移。
指針的應(yīng)用往往與數(shù)組聯(lián)系在一起,為了方便說(shuō)明問(wèn)題,不妨從數(shù)組開(kāi)始解釋指針的偏移。數(shù)組就是許多的變量,它的一個(gè)重要特征就是在內(nèi)存空間中連續(xù)地存放,而且是按下標(biāo)順序存放。比如我們定義一個(gè)有100個(gè)變量的一維整型數(shù)組,它一定從內(nèi)存的某一個(gè)存儲(chǔ)單元開(kāi)始按數(shù)組下標(biāo)順序存放,連續(xù)占用100*4=400字節(jié)。當(dāng)我們定義一個(gè)數(shù)組時(shí),系統(tǒng)就會(huì)自動(dòng)為它分配一個(gè)指針,這個(gè)指針指向數(shù)組的首地址。(在本文剩余部分的論述中,不加區(qū)分地使用“指向數(shù)組的首地址”與“指向數(shù)組的第一個(gè)元素”這兩種說(shuō)法,事實(shí)上這兩種說(shuō)法也是一致的。)
為了讓系統(tǒng)了解每一次指針偏移的單位,也為了方便程序員進(jìn)行指針偏移(讓程序員記住一個(gè)×××變量占用4字節(jié),一個(gè)字符型變量占用1字節(jié)等等是很麻煩的),不用每次去計(jì)算要偏移多少個(gè)字節(jié),C語(yǔ)言引入了指針的基類(lèi)型的概念?;?lèi)型的作用就是讓系統(tǒng)了解某個(gè)指針每次偏移的字節(jié)數(shù)。比如,對(duì)于一個(gè)字符型指針,它每次偏移(比如ptr=ptr+1)所起到的作用就是讓指針偏移1字節(jié);而對(duì)于一個(gè)整型指針,它每次偏移就應(yīng)該是4字節(jié)。這樣操作數(shù)組時(shí)就帶來(lái)了方便。比如對(duì)于一個(gè)指向某個(gè)整型數(shù)組起始存儲(chǔ)單元(稱為首地址)的指針ptr,ptr=ptr+1就表示將該指針指向這個(gè)數(shù)組的下一個(gè)元素的存儲(chǔ)單元,即向后移動(dòng)4字節(jié),而不僅僅是移動(dòng)一個(gè)存儲(chǔ)單元(即移動(dòng)1字節(jié))。
&()、*()、和[ ]運(yùn)算符的意義
在本文中,將&()、*()和[ ]都看成是運(yùn)算符。這樣可以方便理解這三個(gè)概念。簡(jiǎn)單地說(shuō),&()將某個(gè)標(biāo)識(shí)符(比如變量)轉(zhuǎn)化為其在內(nèi)存空間中的地址,而*()是產(chǎn)生一個(gè)對(duì)應(yīng)于某個(gè)地址的標(biāo)識(shí)符,[ ]就更復(fù)雜一點(diǎn),ptr[i]表示將ptr這個(gè)指針虛擬地按其基類(lèi)型進(jìn)行i個(gè)單位的后移,再進(jìn)行*(ptr)運(yùn)算。但這是一個(gè)虛擬的后移,即ptr[i]并不改變ptr的指向,只是將其后移i個(gè)單位并取*()運(yùn)算的結(jié)果算出來(lái)了而已。要改變指針的指向,我們只能通過(guò)類(lèi)似于ptr=ptr+i這樣的語(yǔ)句來(lái)實(shí)現(xiàn)。
實(shí)際中,我們往往不愿意經(jīng)常改變指針的指向,因?yàn)橹羔樀囊苿?dòng)雖然是自由的,但移動(dòng)后往往會(huì)“移不回來(lái)”,因?yàn)槲覀兛赡軣o(wú)法清楚地確定指針的偏移量。后面我們將看到,對(duì)于用指針來(lái)表示的數(shù)組,其元素的引用和賦值是完全可以不用改變指向這個(gè)數(shù)組的首地址的指針指向的,而一旦要改變這個(gè)指針的指向,問(wèn)題就會(huì)變得復(fù)雜一些。
指針類(lèi)型和系統(tǒng)自動(dòng)分配的指針
指針可以指向幾乎所有我們感興趣的程序設(shè)計(jì)要素:函數(shù)、數(shù)組、結(jié)構(gòu)體、鏈表節(jié)點(diǎn)等等。其中不同函數(shù)間往往并不存在嚴(yán)格的線性關(guān)系。鏈表節(jié)點(diǎn)可以根據(jù)算法需要在邏輯上(或物理上)不按線性連續(xù)存儲(chǔ)。但數(shù)組、結(jié)構(gòu)體的共同特征就是它們?cè)谖锢砩隙际蔷€性連續(xù)存儲(chǔ)的。只要指針指向了它們的首地址,就可以通過(guò)簡(jiǎn)單的偏移來(lái)訪問(wèn)各個(gè)它們的元素。指針的偏移性在這兩種數(shù)據(jù)結(jié)構(gòu)中發(fā)揮著至關(guān)重要的作用。這時(shí),我們?cè)倩叵牖?lèi)型的定義目的,就會(huì)有更深層次的認(rèn)識(shí)了。對(duì)于一個(gè)數(shù)組或結(jié)構(gòu)體,它的基類(lèi)型長(zhǎng)度應(yīng)當(dāng)是其元素的長(zhǎng)度(這里的長(zhǎng)度即指在內(nèi)存空間中占用的字節(jié)數(shù)),而不再限于定義為某種簡(jiǎn)單數(shù)據(jù)類(lèi)型的長(zhǎng)度。
在我們定義數(shù)組和函數(shù)時(shí),系統(tǒng)都會(huì)為其自動(dòng)分配一個(gè)指向其首地址的指針。其中,指針在數(shù)組中的應(yīng)用是最頻繁的,也是最基礎(chǔ)的。對(duì)于一個(gè)數(shù)組,其名稱就是一個(gè)指針變量,亦即假如我們定義“int a[10];”的同時(shí)就定義了“int *a=a;”(這只是為了說(shuō)明問(wèn)題,這樣的語(yǔ)句顯然是不合法的)。
數(shù)組應(yīng)用中典型的二級(jí)指針
設(shè)定一個(gè)指向指針的指針,即設(shè)定一個(gè)二級(jí)指針。一般認(rèn)為,指針不宜超過(guò)二級(jí),否則會(huì)大大增加邏輯錯(cuò)誤出現(xiàn)的可能性。
鏈表原理:
舉個(gè)例子,跳馬,依下圖將每一步跳馬之后的位置之后的位置(x,y)放到一個(gè)“結(jié)點(diǎn)”里,再用“鏈子穿起來(lái)”,形成一條鏈,相鄰兩結(jié)點(diǎn)間用一個(gè)指針將兩者連到一起。
為了表示這種既有數(shù)據(jù)又有指針的情況,引入結(jié)構(gòu)這種數(shù)據(jù)類(lèi)型。
依上圖有7個(gè)結(jié)點(diǎn)。
用指針處理鏈表
鏈表是程序設(shè)計(jì)中一種重要的動(dòng)態(tài)數(shù)據(jù)結(jié)構(gòu),它是動(dòng)態(tài)地進(jìn)行存儲(chǔ)分配的一種結(jié)構(gòu)。
動(dòng)態(tài)性體現(xiàn)為:
鏈表中的元素個(gè)數(shù)可以根據(jù)需要增加和減少,不像數(shù)組,在聲明之后就固定不變;
元素的位置可以變化,即可以從某個(gè)位置刪除,然后在插入到一個(gè)新的地方;
結(jié)點(diǎn)里的指針是存放下一個(gè)結(jié)點(diǎn)的地址
鏈表中的元素稱為“結(jié)點(diǎn)”,每個(gè)結(jié)點(diǎn)包括兩個(gè)域:數(shù)據(jù)域和指針域;
單向鏈表通常由一個(gè)頭指針(head),用于指向鏈表頭;
單向鏈表有一個(gè)結(jié)尾點(diǎn),該結(jié)點(diǎn)的指針部分指向一個(gè)空結(jié)點(diǎn)(NULL)。
學(xué)生信息管理系統(tǒng)代碼:
/* *學(xué)生信息管理程序, *管理學(xué)生的個(gè)人信息及各科成績(jī); */ #include<stdio.h> #include<conio.h> #include<string.h> #include<stdlib.h> typedef struct NodeNode; //定義成績(jī)信息節(jié)點(diǎn) //分別為語(yǔ)文、數(shù)學(xué)、英語(yǔ)和總成績(jī); struct Score { int chinese,math,english,sum; }; //定義學(xué)生信息節(jié)點(diǎn) //分別為姓名、班級(jí)、學(xué)號(hào)、成績(jī)和指向下一個(gè)節(jié)點(diǎn)的指針 //定義了4個(gè)全局變量,頭節(jié)點(diǎn),和臨時(shí)節(jié)點(diǎn)變量; struct Node { char name[20],classs[20],number[20]; struct Score score; struct Node* next; }*head,*u,*p,*q; //定義多個(gè)學(xué)生的學(xué)生個(gè)數(shù)及各科平均成績(jī)優(yōu)秀率及格率; intn,C,M,E,Cj,Cy,Mj,My,Ej,Ey; char num[20]; //進(jìn)入菜單函數(shù) void Welcome() { printf("\t\t # # # # # # # # # # # # # # # ##\n"); printf("\t\t # 歡迎您使用學(xué)生成績(jī)管理系統(tǒng) #\n"); printf("\t\t # #\n"); printf("\t\t # 1.讀取文件 #\n"); printf("\t\t # #\n"); printf("\t\t # 2.保存文件 #\n"); printf("\t\t # #\n"); printf("\t\t # 3.添加學(xué)生成績(jī) #\n"); printf("\t\t # #\n"); printf("\t\t # 4.修改學(xué)生成績(jī) #\n"); printf("\t\t # #\n"); printf("\t\t # 5.刪除學(xué)生成績(jī) #\n"); printf("\t\t # #\n"); printf("\t\t # 6.查詢個(gè)人成績(jī) #\n"); printf("\t\t # #\n"); printf("\t\t # 7.查詢本班成績(jī) #\n"); printf("\t\t # #\n"); printf("\t\t # 8.查詢?nèi)3煽?jī) #\n"); printf("\t\t # #\n"); printf("\t\t # 9.退出管理系統(tǒng) #\n"); printf("\t\t # #\n"); printf("\t\t # # # # # # # # # # # # # # # ##\n\n"); printf("\t\t 請(qǐng)輸入指令:(1-9)"); } //構(gòu)造節(jié)點(diǎn)函數(shù) Node* new_node(Node*uu) { uu = (Node*)malloc(sizeof(Node)); uu->next = NULL; return uu; } //添加學(xué)生信息 void Add() { //新建一個(gè)節(jié)點(diǎn); u = new_node(u); printf("\n請(qǐng)輸入您要加入的學(xué)生的信息:\n"); printf("\n姓名: "); scanf("%s",u->name); printf("\n班級(jí): "); scanf("%s",u->classs); printf("\n學(xué)號(hào): "); scanf("%s",u->number); printf("\n語(yǔ)文、數(shù)學(xué)、英語(yǔ)成績(jī): "); scanf("%d%d%d",&u->score.chinese,&u->score.math,&u->score.english); //計(jì)算總成績(jī); u->score.sum = u->score.chinese +u->score.math + u->score.english; //采用頭插法將新節(jié)點(diǎn)的尾指針指向第二個(gè)節(jié)點(diǎn)(掰開(kāi)) u->next = head->next; //將新節(jié)點(diǎn)放在頭節(jié)點(diǎn)后面; head->next = u; printf("\n--->添加成功!\n"); } //根據(jù)學(xué)號(hào)修改信息 //和查找函數(shù)一樣,依次從第二個(gè)節(jié)點(diǎn)開(kāi)始遍歷,如果找到這更新 void Mod() { n = 0; printf("\n請(qǐng)輸入您要修改的學(xué)號(hào): "); scanf("%s",num); for(u = head; u != NULL;u =u->next) { if(strcmp(u->number,num) == 0) { n = 1; printf("\n請(qǐng)輸入新的語(yǔ)文、數(shù)學(xué)、英語(yǔ)成績(jī): "); scanf("%d%d%d",&u->score.chinese,&u->score.math,&u->score.english); u->score.sum =u->score.chinese + u->score.math + u->score.english; printf("\n--->修改成功!\n"); break; } } if(!n) printf("\n--->沒(méi)有這個(gè)學(xué)生的信息!\n"); } //根據(jù)學(xué)號(hào)刪除學(xué)生信息, //從頭節(jié)點(diǎn)開(kāi)始遍歷,如果找到這刪除此節(jié)點(diǎn); void Del() { n = 0; printf("\n請(qǐng)輸入您要?jiǎng)h除的學(xué)生的學(xué)號(hào): "); scanf("%s",num); for(u = head; u != NULL;u =u->next) { if(strcmp(u->number,num) == 0) { n = 1; p->next = u->next; free(u); printf("\n--->刪除成功!\n"); break; } p = u; } if(!n) printf("\n--->沒(méi)有這個(gè)學(xué)生的信息!\n"); } void Sort() { int i,j; //記錄學(xué)生總數(shù); n = 0; for(u = head->next; u != NULL;u =u->next) n++; //采用冒泡法對(duì)各個(gè)節(jié)點(diǎn)按班級(jí)升序和總成績(jī)降序排列 for(i=1;i<=n;i++) { u = head; for(j=0;j<n-i;j++) { p = u->next; q = p->next; if(strcmp(p->classs,q->classs) > 0 ||strcmp(p->classs,q->classs) == 0 && p->score.sum <q->score.sum) { u->next = q; p->next = q->next; q->next = p; } u = u->next; } } } //按學(xué)號(hào)查找某一學(xué)生成績(jī); void Que_One() { //標(biāo)志變量,記錄是否查找成功; n = 0; printf("\n請(qǐng)輸入您要查詢的學(xué)生的學(xué)號(hào): "); scanf("%s",num); //從第二個(gè)節(jié)點(diǎn)開(kāi)始遍歷,直到最后一個(gè)節(jié)點(diǎn)為止; for(u = head->next; u != NULL;u =u->next) { //如果當(dāng)前節(jié)點(diǎn)學(xué)號(hào)與要查找學(xué)號(hào)一致這輸出此學(xué)生信息; if(strcmp(u->number,num) == 0) { n = 1; printf("\n"); puts("班級(jí) 姓名 語(yǔ)文 數(shù)學(xué) 英語(yǔ)總成績(jī)"); printf("%-11s%-15s",u->classs,u->name); printf("%-6d%-6d%-6d%-6d\n",u->score.chinese,u->score.math,u->score.english,u->score.sum); break; } } if(!n) printf("\n--->沒(méi)有這個(gè)學(xué)生的信息!\n"); } void Analyze_Sco(Node*uu) { //對(duì)查找到的節(jié)點(diǎn)進(jìn)行求各科平均成績(jī) //求優(yōu)秀率及格率; C += uu->score.chinese; M += uu->score.math; E += uu->score.english; if(uu->score.chinese >= 60) Cj++; if(uu->score.chinese >= 90) Cy++; if(uu->score.math >= 60) Mj++; if(uu->score.math >= 90) My++; if(uu->score.english >= 60) Ej++; if(uu->score.english >= 90) Ey++; } //打印各科平均成績(jī)及格率優(yōu)秀率 void Print_Sco() { printf("語(yǔ)文平均成績(jī): %-6.2f, 及格率: %%%-6.2f , 優(yōu)秀率:%%%-6.2f.\n\n",(float)C/n,(float)100*Cj/n,(float)100*Cy/n); printf("數(shù)學(xué)平均成績(jī): %-6.2f, 及格率: %%%-6.2f , 優(yōu)秀率:%%%-6.2f.\n\n",(float)M/n,(float)100*Mj/n,(float)100*My/n); printf("英語(yǔ)平均成績(jī): %-6.2f, 及格率: %%%-6.2f , 優(yōu)秀率:%%%-6.2f.\n\n",(float)E/n,(float)100*Ej/n,(float)100*Ey/n); } //查找某一班級(jí)所以學(xué)生的信息; void Que_Cla() { //對(duì)鏈表節(jié)點(diǎn)排序; Sort(); n = C = M = E = Cj = Cy = Mj = My = Ej = Ey= 0; printf("\n請(qǐng)輸入您要查詢的班級(jí): "); scanf("%s",num); printf("\n"); for(u = head->next; u != NULL;u =u->next) { //不是該班的學(xué)生則跳過(guò); if(strcmp(u->classs,num)) continue; //如果是第一個(gè)學(xué)生則打印頭信息 if(!n) puts("學(xué)號(hào) 姓名 語(yǔ)文 數(shù)學(xué) 英語(yǔ)總成績(jī)"); n++; printf("%-11s%-15s",u->number,u->name); printf("%-6d%-6d%-6d%-d\n",u->score.chinese,u->score.math,u->score.english,u->score.sum); Analyze_Sco(u); } if(!n) { printf("沒(méi)有這個(gè)班級(jí)的學(xué)生信息!\n"); return ; } //打印該班級(jí)學(xué)生的各個(gè)成績(jī)的特征值; printf("\n該班共有學(xué)生 %d 人.\n\n",n); Print_Sco(); } //打印全校所以學(xué)生的信息 //具體情況同打印班級(jí)學(xué)生信息; void Que_All() { Sort(); n = C = M = E = Cj = Cy = Mj = My = Ej = Ey= 0; printf("\n"); if(head->next == NULL) { printf("--->沒(méi)有學(xué)生信息!\n"); return ; } puts("班級(jí) 學(xué)號(hào) 姓名 語(yǔ)文 數(shù)學(xué) 英語(yǔ)總成績(jī)"); for(u = head->next; u != NULL;u =u->next) { n++; printf("%-12s%-12s%-15s",u->classs,u->number,u->name); printf("%-6d%-6d%-6d%-d\n",u->score.chinese,u->score.math,u->score.english,u->score.sum); Analyze_Sco(u); } printf("\n全校共有學(xué)生 %d 人.\n\n",n); Print_Sco(); } //保存文件; void Save() { char c; printf("\n確認(rèn)保存?(Y/N): "); scanf("%*c%c",&c); if(c == 'N') return ; FILE *fp; if((fp=fopen("C:\\data.txt","w"))==NULL) { printf("\n--->無(wú)法打開(kāi)文件\n"); return ; } //寫(xiě)入數(shù)據(jù)表頭信息; fputs("班級(jí) 學(xué)號(hào) 姓名 語(yǔ)文 數(shù)學(xué) 英語(yǔ)總成績(jī)",fp); if(head->next != NULL) fputs("\n",fp); //從頭節(jié)點(diǎn)開(kāi)始依次寫(xiě)入文件; for(u = head->next; u != NULL;u = u->next) { fprintf(fp,"%-11s%-11s%-15s",u->classs,u->number,u->name); fprintf(fp,"%-6d%-6d%-6d%-d",u->score.chinese,u->score.math,u->score.english,u->score.sum); if(u->next != NULL) fprintf(fp,"\n"); } fclose(fp); printf("\n--->成績(jī)成功存入C:\\\\data.txt中\(zhòng)n"); } //讀取文件; void Open() { printf("\n請(qǐng)把數(shù)據(jù)放到目錄C:\\\\data.txt中,按任意鍵確認(rèn).\n"); getch(); FILE *fp; //從c盤(pán)根目錄下讀取文件; if((fp=fopen("C:\\data.txt","r"))==NULL) { printf("\n--->沒(méi)有找到文件!\n"); return ; } char tmp[100]; //讀取65個(gè)菜單頭字符存入tem字符數(shù)組中; fgets(tmp,66,fp); //讀到文件結(jié)尾處跳出循環(huán); while(!feof(fp)) { u = new_node(u); fscanf(fp,"%s%s%s",u->classs,u->number,u->name); fscanf(fp,"%d%d%d%d",&u->score.chinese,&u->score.math,&u->score.english,&u->score.sum); //頭插法建立鏈表; u->next = head->next; head->next = u; } printf("\n--->成績(jī)讀入成功!\n"); fclose(fp); } //退出程序 void Exi() { char c; printf("\n確定退出?(Y/N): "); scanf("%*c%c",&c); if(c == 'N') return ; //打印結(jié)束語(yǔ); system("cls"); printf("\n\n"); printf("\t\t\t %c %c %c %c %c %c %c %c%c\n",4,4,4,4,4,4,4,4,4); printf("\t\t\t %c 謝謝使用%c\n",4,4); printf("\t\t\t %c %c %c %c %c %c %c %c%c\n",4,4,4,4,4,4,4,4,4); printf("\t\t\t Thankyou!\n\n\n"); exit(0); } int main() { //存儲(chǔ)指令的變量 int orz; //設(shè)置系統(tǒng)文本顏色 system("color 0B"); //新建一個(gè)學(xué)生信息頭節(jié)點(diǎn); head = new_node(head); while(1) { //顯示菜單、 Welcome(); //接收用戶命令、 scanf("%d",&orz); //調(diào)用系統(tǒng)函數(shù)清屏; system("cls"); switch(orz) { //根據(jù)指令進(jìn)入相應(yīng)菜單選項(xiàng) case 1:Open();break; case 2:Save();break; case 3:Add();break; case 4:Mod();break; case 5:Del();break; case 6:Que_One();break; case 7:Que_Cla();break; case 8:Que_All();break; case 9:Exi();break; default :printf("\n--->無(wú)效的指令!\n"); } printf("\n"); //執(zhí)行系統(tǒng)函數(shù) system("pause"); system("cls"); } return 0; }
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。