函式與指標
阿新 • • 發佈:2020-12-28
在C語言中,不僅是變數,陣列,有地址,其實我們的函式也有地址 有地址的東西,我們就可以用一個指標變數來儲存它們的地址,並且 可以通過該指標去訪問指向的物件 函式也有地址--->函式指標 11.函式指標 函式指標是一個指標,只不過該指標指向的是一個函式。 1)函式指標如何定義呢 指向的型別 *指標的變數名; int sum(int,int); int sum(int a,int b) { } 要定義一個指標變數p,來儲存函式sum的地址,怎麼定義呢? 指向的型別 *p; typeof(sum) *p; 如何來描述一個函式的型別的? 函式的型別 三要素: 函式的返回值 函式名(函式的引數型別列表) int (int,int)==>就是描述一個類似sum函式的 返回值型別 (形參的型別列表) 如: int *abc(int a,float b) { } 描述abc的型別: int *(int,float) ->是一個返回int*型別名,帶一個int,一個float的函式 型別。 int sum(int a,int b) { } 描述sum的型別 int (int,int) typeof(sum) *p; ==> int (int,int) *p; int (*p)(int,int); 函式指標的定義方法: 指向函式的返回值型別 (*指標變數名) (指向函式的形式引數型別列表); 練習: 1.請定義一個指標變數p,來儲存函式abc的地址 int *abc(int a,int *b) { } int* (*p)(int,int *); 2.請定義一個指標變數p,來儲存如下函式的地址 int sum_array(int *a,int n) //int a[10];int *p=a; //*(p+i) <==> p[i] int sum_array(int a[],int n) { } int (*p)(int *,int); 陣列名作為函式的引數(形參,實參),當作一個指標來用的。 2)怎麼給函式指標賦值呢? p=函式的地址 函式的地址怎麼獲取呢? &物件名 => 取物件的地址 &函式名 函式名:在C語言中,函式名本身就代表函式的首地址《------------- 練習: int sum_array(int *a,int n) { } //定義一個函式指標p 指向sum_array int (*p)(int *,int)=&sum_array; int (*p)(int *,int)=sum_array; <==> int (*p)(int *,int); p=&sum_array; int (*p)(int *,int); p=sum_array;; <==> int *p=&a; int *p; p=&a; p指向函式sum_array 3)怎麼通過函式指標去呼叫指向的函式呢? p=&sum_array *p=>*&sum_array==>sum_array p=sum_array p=>sum_array 通過函式指標去呼叫指向的函式。有如下兩種方式: p為函式指標名 1)(*p)(實參列表) 2)p(實參列表) 練習: 1.在main中定義了一個函式指標p,通過p去呼叫sum_array 求陣列元素的和-->sum_array ==>一維陣列 int sum_array(int a[],int n); int sum_array(int *a,int n)//int a[10]; a typeof(a) typeof(&a[0)) =>tyepof(a[0]) *=>int* { int i; int sum =0; for(i=0;i<n;i++) { sum+=a[i];//*(a+i) <==> a[i] } return sum; } main() { int a[10]={.....}; int (*p)(int* ,int)=sum_array; int sum=p(a,10); //int (*p)(int* ,int)=&sum_array; //int sum=(*p)(a,10); } 2.在main中定義了一個函式指標p,通過p去呼叫xxx_num ==>二維陣列 int xxx_num(int (*a)[4],int n); int xxx_num(int a[][4],int n) { printf("恭喜你呼叫成功!\n"); return 0; } int (*p)(int (*)[4],int); ---------------------------------------------------- int a[3][4]; => int[4] a[3]; a[0] //一維陣列 a[0]是這個一維陣列的名字 //是一個含有4個int型別元素的一維陣列 a[1] a[2] int[4] a[] 陣列當作函式的引數的時候,形參一般這樣子設計: 陣列元素的型別 陣列名[],元素的個數 int a[][4],int n (1) 陣列名當函式引數,當指標來看: a=>&a[0] typeof(a)==>typeof(&a[0])==>typeof(a[0]) * =>int[4] * 陣列元素的型別 *指標變數名,元素的個數 (2) int (*a)[4], int n(2) --------------------------------------------------------------------- 12.二級指標與多級指標 int a; 可以定義一個指標變數p來儲存a的地址 typeof(a) *p;//指向型別 *指標變數; int *p; p=&a;//p->a *p=>*&a==>a p本身也有地址,我們可以定義一個指標變數p2來儲存p的地址 typeof(p) * p2; int ** p2; //p2二級指標,它儲存的是一個一級指標的地址 //有人說,我要分清到底是兩級指標還是一級指標,怎麼區分呢? //QNMLB,你只要知道它是一個指標(並且它儲存了誰的地址)就可以了 p2=&p; *p2 =>*&p=>p **p2 ==> **&p =>*p=>a a=1024; *p=1024; **p2=1024; ------------------------ main() { int n; scanf("%d",&n); int a[n];//動態陣列,根據使用者輸入,陣列元素的個數動態改變的。 } “如何來實現動態陣列的分配呢?” 13.動態記憶體分配函式 malloc/realloc/calloc free NAME malloc, free, calloc, realloc - allocate and free dynamic memory #include <stdlib.h> void *malloc(size_t size); malloc: memory allocate 記憶體分配 malloc用來動態分配一個size大小的記憶體空間,並且把分配到記憶體空間 的首地址返回。malloc分配的空間的生存期,是隨程序持續性。 malloc分配的空間一旦分配給你,它不會自動釋放,一定要手動呼叫 free或這個程序消亡了。 size:要分配的空間的大小(以位元組為單位) 返回值: 成功返回分配到的空間的首地址, 失敗返回NULL; 例子: int a[n]; ==> int *p=(int*)malloc(n*sizeof(int)) p[0] p[1] .. *(p+i) malloc可能會導致記憶體洩漏(產生一些垃圾記憶體) int *p=malloc(100);//p指向一個100位元組的記憶體空間 //通過p指標操作訪問這段空間 p=&a;//p指向a,那100位元組的空間,就沒有指向指向了它 ... 這100個位元組的空間,是不是訪問不了啦,但是這段空間 又不能分配給別人,這樣的記憶體,我們稱之為“垃圾記憶體” 把這種現象稱之為“記憶體洩漏” java 垃圾回收機制!!! void free(void *ptr); free 用來釋放ptr所指向的記憶體的 ptr必須是malloc/calloc/realloc這是三個函式申請的記憶體 ------------------------------------------------------------------ void *calloc(size_t nmemb, size_t size); 作用型別malloc,不過它是陣列分配函式, 它分配一個數組空間,它帶兩個引數 n表示分配多少個元素 size表示每個元素佔多少位元組 so.它共分配的連續空間的大小:size*n 並且calloc分配空間,它把空間的內容全部置0、 =>malloc(n*size) 清0 ------------------------------------------------------------------ void *realloc(void *ptr, size_t size); re:repeat 重複 realloc用來把ptr(由malloc/calloc/realloc返回動態記憶體的地址) 指向的動態記憶體,擴充套件到size大小 ptr == NULL realloc(NULL,size)<==>malloc(size); ptr != NULL 擴充套件 1.size>原來的大小 realloc用來把ptr指向的記憶體,擴充套件到size位元組,原來的前面的記憶體 內容保持不變,後面的新增的記憶體內容不會初始化 a.原址擴建 b.整體搬遷 2.size == 0 realloc(ptr,0)<==>free(ptr) 3.size<原來的大小 這種情況,作者自己都沒有考慮到這種行為。 結果未定義的(undefined ,什麼結果都有可能發生); 14.陣列作為函式引數的問題 把陣列當作函式引數: (1)陣列元素型別 陣列名[],元素的個數 (2)陣列元素型別 *指標變數名,元素的個數 1) int a[10] func(int p[],int n); func(int*p,int n); 2) int a[3][4]; int[4] p[],int n==> int p[][4],int n int[4]* p,int n==>int (*p)[4],int n 15.main的引數問題 在linux下面,程式執行的時候,可以帶引數,只不過所有的引數都當作是字串 來處理。 如: ./main 123 456 "abc efg" 引數多個字串==>字串的陣列 char *argv[]={"./main","123","456","abc efg"}; 在執行main函式的時候,可以把上面那個陣列傳遞給main函式, so,linux下面的C程式的main的引數可以如下定義: 元素個數,數字元素型別 陣列名[] or 元素個數,陣列元素型別 *指標變數名 //char * *argv int main(int argc,char *argv[]) or int main(int argc,char** argv) { } 練習: 請使用主函式的引數計算兩個數的和 積 ./main 456 + 789 => ./main 456 * 789 => 總結: 1.指向 如果p儲存了a的地址,那麼我們就說p指向a p指向的型別就是a的型別 指標變數的定義 指向的型別 * 指標變數名; &x:指標,因為儲存了x的地址 typeof(&x)=> typeof(x) * 2.&地址 <==>地址對應的那個物件(變數,函式) *&a <==> a 3. *(a+i) <==> a[i],when i>=0 例子: p[3][4]<==>*(p[3]+4]<==>*(*(p+3)+4) 4.指標作加減的問題 p+i(p是一個指標,i是一個整數) 不是簡單的加減數值,而是加減i個指向單元的長度 5.陣列名的問題 陣列名可以看作是指向第一個元素型別的常量指標 陣列名在數值上為第一個元素的地址 陣列名a,在程式碼中有兩個意思 1)代表整個陣列 typeof(a) siezof(a) &a 2)在合適的情況下,可以當作指標看 &a[0] 6.指標陣列與陣列指標 主函式的引數的那個陣列 是指標陣列 7.字串API函式 strlen strcpy strncpy strcmp/strncmp strcat/strncat 8.函式指標 9.動態記憶體 malloc/realloc/calloc/free 作業: 0.前面的那套試卷 陣列和函式 輸入 ./a.out 3 + 5 ,輸出 8 。(3位數以上 8位數以下的數)(7/2 = 3.5)輸出字串 1 . 字串的大小寫轉換:請編寫一個函式,實現講字串的大小寫轉換功能,數字不變。 例:輸入 AD5ChadCtT 輸出 ad5cHADcTt 2. 字串的倒序 : 請編寫一個函式,實現字串的倒序輸出。 例:輸入 woshishazi520 輸出 025izahsihsow 3. 刪除字串中的子串 :請編寫一個函式,實現下面的功能: 輸入2個字串S1和S2,要求刪除字串S1中出現的所有子串S2,即結果字串中不能包含S2。 例 輸入: Tomcat is a male ccatat cat 輸出: Tom is a male 4. 字串的迴圈左移:請編寫一個函式,實現下面的要求: 輸入:輸入在第1行中給出一個不超過100個字元長度的、以回車結束的非空字串;第2行給出非負整數N。 例: Hello World! 3 輸出:在一行中輸出迴圈左移N次後的字串。 例 :lo World!Hel 5. 字串的最長對稱子串。請編寫一個函式,得到一個字串的最長對稱子串及其長度:(考慮奇數和偶數的情況) 例 輸入: nihaoaoahhaoin 輸出: haoaoah 7 6.經典混合題 #include<stdio.h> #include<windows.h> int main() { char *c[] = { "ENIER","NEW","POINT","FIRST" }; char **cp[] = { c + 3,c + 2,c + 1,c }; char ***cpp = cp; printf("%s\n", **++cpp); printf("%s\n", *--*++cpp + 3); printf("%s\n", *cpp[-2] + 3); system("pause");//暫停 等待 return 0; }