1. 程式人生 > 其它 >函式與指標

函式與指標

技術標籤:學習c語言指標

在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;
		}