《C語言指針講解》PPT課件.ppt_第1頁
《C語言指針講解》PPT課件.ppt_第2頁
《C語言指針講解》PPT課件.ppt_第3頁
《C語言指針講解》PPT課件.ppt_第4頁
《C語言指針講解》PPT課件.ppt_第5頁
已閱讀5頁,還剩58頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、C語言程序設計指針,林大 經管學院 瞿華,指針,指針是啥? 指針基本運算 函數中的指針 指針與數組 指針與動態(tài)數組 指針與結構 函數指針 指針數組與多重指針 實驗,一、指針是啥,什么是指針? 假設Z同學是我們班作業(yè)做得最好的同學,大家都喜歡參考他的作業(yè)。 Z=作業(yè)的標準答案 可以把Z看作是一個作業(yè)類型的普通變量 某天A同學找Z:“把作業(yè)借我參考一下?”周X回答:“標準答案在Y同學那里,你找他吧” Z=“標準答案在Y那里” 這時候Z相當于一個指向別人(Y)的鏈接, 他就變成了一個指針!,一、指針是啥,指針(Point)實際上就是一個鏈接! 指針變量中保存的內容是一個內存地址。 例: int y=

2、10; int *z= 指針z的內容就是y所在的內存地址。 “z,標準答案是多少?” z說:你去問y吧! y說:標準答案是10!,一、指針是啥,int y=10; int *z=,10,y,內存地址1008,1008,z,內存地址1004,z指向y,二、指針基本運算,要學好指針,必須熟練掌握指針相關的運算。(接下來會提問!)如: 指針的定義方法:在變量標識符前加* int *p,a,b; /定義了一個整型指針p和一個普通變量a /將30賦給p指向的內存中 對于指針p,*p相當于一個普通變量! 見01points.c(使用調試單步執(zhí)行查看結果),2.1 ,?,a,內存地址1008,?,b,內存地

3、址1004,?,p,內存地址1000,2.1 a=10;,?,a,內存地址1008,?,b,內存地址1004,?,p,內存地址1000,2.1 a=10;,10,a,內存地址1008,?,b,內存地址1004,?,p,內存地址1000,2.1 a=10; p=,10,a,內存地址1008,?,b,內存地址1004,?,p,內存地址1000,2.1 a=10; p=,10,a,內存地址1008,?,b,內存地址1004,1000,p,內存地址1000,2.1 a=10; p=,10,a,內存地址1008,?,b,內存地址1004,1000,p,內存地址1000,2.1 a=10; p=,10,a

4、,內存地址1008,10,b,內存地址1004,1000,p,內存地址1000,2.1 a=10; p=,10,a,內存地址1008,10,b,內存地址1004,1000,p,內存地址1000,2.1 a=10; p=,20,a,內存地址1008,10,b,內存地址1004,1000,p,內存地址1000,2.1 a=10; p=,20,a,內存地址1008,10,b,內存地址1004,1000,p,內存地址1000,2.1 a=10; p=,21,a,內存地址1008,10,b,內存地址1004,1000,p,內存地址1000,2.1 double *p2; sizeof(p1)和sizeo

5、f(p2)哪個大?為什么? 因為我們使用的是32位的gcc編譯器(dev-cpp自帶),所以每個指針的大小都是32位,即4個字節(jié)。 見04size.c,三、函數中的指針,折騰指針有啥用呢? 回憶一下函數的知識: 在函數中改變形式參數的值,會影響實際參數嗎?為什么? 見05swap_1.c 由于: 函數的形式參數實質上是一個局部變量 函數執(zhí)行時先將實際參數的值賦給形式參數 所以在函數中改變形式參數,不會影響實際參數,三、函數中的指針,swap(i,t)就相當于: int a=i; int b=t; int temp=a; a=b; b=temp; 很顯然,i和t的值實際并未改變。,3.1 指針類

6、型參數,可是我確實需要在函數中改變實參的值,怎么辦? 使用指針類型參數,就可以解決這個問題。 見05swap_2.c 為什么? 在函數swap(int *p1,int *p2)中,p1和p2是兩個指針類型的形式參數。 注意,在函數中,p1和p2各自指向哪里有變化嗎? 變化的是*p1和*p2,即p1和p2指向的內存地址中保存的數據!,3.1 指針類型參數,swap( 很顯然,這樣是能交換i和t的內容的。,3.1 指針類型參數,使用指針參數的另外一個好處是節(jié)約??臻g。 見06stacksize.c。程序為什么會異常關閉? 由于函數調用時會為struct參數分配很大一塊內存(1M字節(jié)),超過了棧的現(xiàn)

7、有空間大小,導致堆棧溢出。 見06stacksize_2.c,改用struct指針作為參數。 函數調用時只需要為指針參數分配一塊內存(還記得一個指針占用多大空間嗎?),所以就不存在堆棧溢出問題了!,3.2 指針類型返回值,函數也可以返回指針類型的數據。 例:07pointreturn.c 思考:可以返回一個指向函數局部變量的指針嗎?為什么? 見08localpoint.c 因為函數執(zhí)行完畢后,其局部變量的生命就結束了!對應的內存空間可能會被別的變量使用! Never ever返回指向函數局部變量的指針!,四、指針與數組,指針變量中保存的是地址。 也可以理解為,指針就是地址類型的變量。 數組實質

8、上是地址類型的常量。 因此: 可以將數組地址賦給指針 也可以直接使用下標來訪問指針指向的數組地址。 同樣,可以使用*運算符來訪問數組的第一個元素 見09arraypoint.c,4.2數組元素訪問與指針,如果希望指針p指向a的第4個元素的地址,怎么辦? 方法1:使用 方法2:使用+運算符 p=a+3; 方法2寫起來更簡單,因此更常見。 思考:想得到字符串str從第3個字符開始的字串,怎么辦? 假設str為”12345678”,想得到”45678”。 見10substring.c,4.3 指針地址運算,指針(數組)可以與整數做加減運算,得到的是一個新的地址 假設p指向一個數組,那么: p+1指向

9、數組中的下一個元素。 p-1指向數組中的上一個元素。 p+4指向數組中的后面第4個元素 p-4指向數組中的前面第4個元素 p+令p指向數組中的下一個元素 p令p指向數組中的上一個元素 見11pointmath.c,4.3 指針地址運算,C語言中經常利用地址運算來處理字符串 例:模擬實現(xiàn)strcpy函數 見例12strcpy.c 注意,該程序使用了多個技巧,請認真思考體會: 字符串以0結尾 C語言中將0作為邏輯假,非0作為邏輯真 指針算數+; 函數形參改變不影響實參,4.3 指針地址運算,在C語言中,兩個同類型的指針可以做減法。 要讓兩個指針的減法p2-p1產生有意義的結果,需要滿足下面兩個條件

10、: p2和p1指向同一個數組的不同元素 p2指向的元素與p1相同,或者在p1指向的元素之后 則p2-p1表示p2和p1之間差多少個元素,即p2指向的元素與p1指向的元素的下標之差 見13strlen.c,計算字符串長度,4.4 數組參數與指針,在函數定義中,將參數類型定義為指針或者定義為數組,效果都是一樣的 實際都是定義了一個指針類型的參數。 這是C語言的一個語法糖 /syntax sugar 同樣,也可以將數組作為實參傳遞給指針類型的形參 見例14arrayparam.c,4.5 字符串與指針,使用字符數組與字符指針,最大的區(qū)別在于C對它們的初始化的處理上。 例如: char str=“He

11、llo world!”; char s*=“Hello world!”; C會為str在棧上分配一塊13字節(jié)大小的內存,并將“Hello world!”這13個字符拷貝到這塊內存中。 相當于:char str13;strcpy(str,”Hello world!”); 而s只是在棧上分配了4字節(jié)大小的內存,然后把“Hello world!”在程序的常量數據區(qū)中的地址保存在s中。,4.5 字符串與指針,由于程序的常量區(qū)是不可寫的,所以在程序中可以修改str的內容,而修改s的內容就會出錯! 見:15strchange.c,4.6 練習,已知: int a=0,1,2,3,4,5,6,7,8,9,1

12、0; int *p1=a,*p2=a+10,*p3; 求下列運算的結果:,(7)*(p1+3)+*(a+5) 8 (8)p14+a610 (9)p3=p1;p3+;*p31 (10)p3=a+6;p3-p1;6 (11)p1=a 0 (12)p2=p1 1,(1)p2-p1 10 (2)*(p1) 0 (3)p100 (4)*a0 (5)*(a+5)5 (6)*(p2-3)7,五、指針與動態(tài)數組,除了用作函數參數外,指針的另一個重要作用是用來實現(xiàn)動態(tài)內存管理。 所謂動態(tài)內存,就是指大小、生存期都由你的代碼自己來控制一塊內存。 主要涉及兩個函數,即malloc/表示內存分配和free。/表示內存

13、釋放 malloc:在堆(heap)中分配一塊指定大小的內存 free:釋放之前由malloc分配的內存。 堆的大小只受到操作系統(tǒng)尋址能力的限制(32位指針最大只能表示4G內存) 動態(tài)內存最常見的用途之一是實現(xiàn)動態(tài)數組。,五、指針與動態(tài)數組,普通數組主要有兩個缺陷: 數組的大小是程序編寫時決定的。為了保證數組的大小夠用,通常需要將數組定得很大,導致內存的浪費。 數組的大小受到棧空間大小的限制。 使用動態(tài)數組就可以解決這些問題。 見17dynamicarray.c 注意:使用動態(tài)內存分配時,一定要注意及時free不需要的內存。否則就會造成內存泄漏。,六、指針與結構,指針除了可以用于指向基本數據類

14、型的地址外,也可以指向結構等自定義類型數據的地址。 結構指針在使用上和普通類型指針沒有區(qū)別。 可以先用*取指針對應結構的內容,然后用.來訪問其成員,如(*p).name 但也可以直接使用-訪問結構指針成員,這種用法更常見,如:p-name 例:見18structpoints.c,6.1 結構指針與參數,使用結構指針來作為函數的形參是一種常見的函數定義方式。 好處: 利用結構的成員,一次可以傳入多項數據,從而簡化了函數的定義和調用 使用結構指針做形參,可以在函數中修改結構的成員 使用結構指針做形參,可以減少函數調用時參數內容復制的開銷。 后面我們會大量使用結構指針來做形參!,6.2 鏈表,利用結

15、構指針,可以實現(xiàn)一種重要的數據結構鏈表。 使用動態(tài)數組,我們可以解決程序編寫時,不知道實際元素的數量導致的??臻g浪費問題。 但動態(tài)數組依然需要在為數組分配空間之前知道元素的數量! 如果在實際使用中,實際元素的數量是經常動態(tài)變化的,那么動態(tài)數組依然會有內存浪費的問題。,6.2 鏈表,數組還存在另一個缺點,就是刪除和插入元素的效率問題。 如果要將一個新元素插入到數組的第3個位置上,那么從3到最后的全部元素都必須向后移動一位 同樣,刪除元素需要將后面的全部元素向前移動一位 鏈表解決了數組的這些缺點。它也是一種常用的數據結構。它的特點是: 能夠動態(tài)增加或減少元素 插入或刪除某個元素,只會影響和它相鄰的

16、兩個元素。 搞明白鏈表及其操作,指針就徹底學會了!,6.2 鏈表,鏈表(Chained List/Linked List)在形式上類似于現(xiàn)實中的鏈條: 由多個結點(node)組成 每個結點中除數據外,還包含一個指針,指向鏈表中的下一個結點。 通過指針,各結點依次連接成一個鏈表 我們利用圖形的形式來看一下鏈表,6.2 鏈表,哨兵,結點1,結點2,結點3,結點4,結點5,NULL,鏈表頭指針head,單向鏈表的基本形式,struct node int data; struct node* next; ;,6.2 鏈表,哨兵,結點1,結點2,結點3,結點4,結點5,NULL,鏈表頭指針head,指針

17、變量p,struct node int data; struct node* next; ;,p=head-next; while(p-next!=NULL) /訪問數據p-data p=p-next; ,遍歷鏈表,6.2 鏈表,結點1,結點2,結點3,結點4,結點5,NULL,鏈表頭指針head,指針變量p,struct node int data; struct node* next; ;,p=head-next; while(p-next!=NULL) /訪問數據p-data p=p-next; ,遍歷鏈表,哨兵,6.2 鏈表,結點1,結點2,結點3,結點4,結點5,NULL,鏈表頭指針

18、head,指針變量p,struct node int data; struct node* next; ;,p=head-next; while(p-next!=NULL) /訪問數據p-data p=p-next; ,遍歷鏈表,哨兵,6.2 鏈表,結點1,結點2,結點3,結點4,結點5,NULL,鏈表頭指針head,指針變量p,struct node int data; struct node* next; ;,p=head-next; while(p-next!=NULL) /訪問數據p-data p=p-next; ,遍歷鏈表,哨兵,6.2 鏈表,結點1,結點2,結點3,結點4,結點5,

19、NULL,鏈表頭指針head,指針變量p,struct node int data; struct node* next; ;,p=head-next; while(p-next!=NULL) /訪問數據p-data p=p-next; ,遍歷鏈表,哨兵,6.2 鏈表,結點1,結點2,結點3,結點4,結點5,NULL,鏈表頭指針head,遍歷鏈表,指針變量p,struct node int data; struct node* next; ;,p=head-next; while(p-next!=NULL) /訪問數據p-data p=p-next; ,NULL,哨兵,6.2 鏈表,哨兵,結點

20、1,結點2,結點3,結點4,結點5,鏈表頭指針head,新節(jié)點,struct node int data; struct node* next; ;,先使用遍歷操作找到接點2; p2-next=p1-next;,插入新結點到結點2、3之間,指針變量p1,指針變量p2,NULL,NULL,6.2 鏈表,結點1,結點2,結點3,結點4,結點5,鏈表頭指針head,新節(jié)點,struct node int data; struct node* next; ;,先使用遍歷操作找到接點2; p2-next=p1-next; p1-next-p2;,插入新結點到結點2、3之間,指針變量p1,指針變量p2,N

21、ULL,哨兵,6.2 鏈表,結點1,結點2,結點3,結點4,結點5,鏈表頭指針head,新節(jié)點,struct node int data; struct node* next; ;,先使用遍歷操作找到接點2; p2-next=p1-next; p1-next-p2; 整理一下圖形,插入新結點到結點2、3之間,指針變量p1,指針變量p2,NULL,哨兵,6.2 鏈表,結點1,結點2,結點3,結點4,結點5,鏈表頭指針head,新節(jié)點,struct node int data; struct node* next; ;,先使用遍歷操作找到接點2; p2-next=p1-next; p1-next-

22、p2; 整理一下圖形,插入新結點到結點2、3之間,NULL,哨兵,6.2 鏈表,哨兵,結點1,結點2,結點3,結點4,結點5,NULL,鏈表頭指針head,指針變量p1,struct node int data; struct node* next; ;,令p1指向結點1,p2指向結點2 p1-next=p2-next;,刪除結點2,指針變量p2,6.2 鏈表,結點1,結點2,結點3,結點4,結點5,NULL,鏈表頭指針head,指針變量p1,struct node int data; struct node* next; ;,令p1指向結點1,p2指向結點2 p1-next=p2-next;

23、 整理圖形,刪除結點2,指針變量p2,哨兵,6.2 鏈表,結點1,結點3,結點4,結點5,NULL,鏈表頭指針head,struct node int data; struct node* next; ;,令p1指向結點1,p2指向結點2 p1-next=p2-next;,刪除結點2,哨兵,6.2 鏈表,在單向鏈表的基礎上,還可以進一步實現(xiàn)雙向鏈表、循環(huán)鏈表等更復雜的數據結構。 例:輸入學生信息,學號為0時結束輸入。保存并輸出按照學號排序的信息。然后用戶每輸入一個名字,從存儲中刪除對應的信息,直到用戶輸入回車或存儲為空為止。 分析: 使用鏈表保存學生信息 每輸入一個學生信息,動態(tài)建立一個結點,并插入到合適的位置 最后使用遍歷操作來輸出學生信息 例:19studentlink.c,七、函數指針,指針實質上是一個內存地址。這個地址不但可以是數據的存放地址,也可以是一段代碼的地址。 函數指針就是一個指向函數地址的指針,即指向代碼的指針。 函數指針的定義及使用見20funcpoints.c 利用函數指針,可以實現(xiàn)很多非常強大的功能。,7.1 函數指針做參數,使用函數指針做函數的參數,可以用一個函數實現(xiàn)多個功能 例:學生信

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論