序
指針說:love me,love me!
但是他對指針說:I hate u,I hate u!
……
?
指針僅僅是作為指針,我們可以把它當做有用的工具,為我們提供便利與好處。說起工具不得不讓我想起一樣東西——鋤頭,因為原人類有了鋤頭才使人類文明進入了開荒造田的農業時代,解決了溫飽,開啟了人類新紀元。可以這么說吧鋤頭使人類文明得到進步,沒有鋤頭也就沒有今天的我們,其地位與重要性可想而知。那么我們的指針何以能發揮像鋤頭那樣驚人的魅力呢?
?
魅力1 算法之找我的名字——簡單,靈活,快捷
以下算法功能是在一個字符串中查找長度為8的一個字符子串,比如我的名字“ZhanHang”就是一個8長度的字串。算法解釋:因為字串myname的長度為8,也就是它是一個8字節的內存連續的數組,而myname指向這一段內存。又Long long 指針類型是一個指向8字節內存的類型,因此就可以將myname轉換成long long 類型指針,如此在進行子串的比較時,就可以直接比較兩個long long 類型的變量即可,免去了對子串進行遍歷的麻煩。詳情請看代碼。
- #include?<stdio.h>??
- ??
- int?find_my_name(const?char*?str,?int?str_len,?const?char*?myname)??
- {??
- ????long?long?*pkey?=?(long?long*)myname;??
- ????long?long?*curr_str?=?NULL;??
- ????str_len?=?str_len?-?sizeof(long?long);?//此處是為了for循環作的優化處理,因為后面7個字符不需要遍歷檢測了??
- ????for(int?i=0;?i<=str_len;?i++)??
- ????{??
- ????????curr_str?=?(long?long*)&str[i];?//得到一個新的子串??
- ????????if(?*pkey?==??*curr_str?)?//判斷兩個字串是否相等??
- ????????????return?i+1;??//返回子串在字串中的位置??
- ????}??
- }??
- /**?
- Author:花心龜?
- Blog:http://blog.csdn.net/zhanxinhang?
- **/???
- int?main()??
- {??
- ????char?str[]="alZhanhangadf";??
- ????char?myname[]="Zhanhang";??
- ????printf("my?name?at?%d\n",find_my_name(str,sizeof(str)/sizeof(char),myname));//輸出結果是3??
- ??????
- ????return?0;??
- }??
(題外語:作者使用此實例旨在通過靈活應用指針的例子,幫助理解指針,并通過理解指針作為本文介紹指針魅力的起點,實際工程中并不贊同此方法,因為本方法存在擴展性差,移植性差等問題。)
魅力2 迭代器——高效
“迭代”一詞在漢語里面是“更替”的意思,也就是說“迭代器”的意思就是指用來做更替操作的工具。實例:實現算法使一個字符串顛倒順序,實現步驟:構建兩個迭代器p 和 q ,在一次遍歷中,p的位置從字串開頭向中間不斷更替,q從字串末尾向中間不斷更替,
然后每次交換p和q所指向的內容即可,直到p和q在中間相遇,這時循環次數剛好等于字串的長度_l/2。
實現代碼:
- void?reverse(char?*_str,int?_l)?//反轉函數,_l指要反轉字串的長度??
- {??
- ?char?*p=_str,*q=_str+_l-1,temp;??
- ??
- ?while(q>p)??
- ???{???
- ?????temp=*p;??
- ?????*p=*q;??
- ?????*q=temp;??
- ???
- ?????p++;??
- ?????q--;??
- ???}??
- }??
我若不用迭代器p和q呢?用兩個變量i和j作為str的下標,也就是說訪問元素的方式變為:str[i],str[j],如下
- void?reverse(char?*_str,int?_l)?//反轉函數,_l指要反轉字串的長度??
- {??
- ??int?i=0,j=_l-1,temp;??
- ???
- ?while(j>i)??
- ???{???
- ?????temp=str[i];??
- ?????str[i]=str[j];??
- ?????str[j]=str[i];??
- ???
- ?????i++;??
- ?????j--;??????
- ???}??
- }??
?
?
魅力3 函數指針——高擴展性
何為函數指針:函數指針就是指向某個函數的指針,指針變量存儲的是函數的地址。
如何定義一個函數指針:定義為 [ 返回類型 (*pfun) (形參,…) ]。
如何使用函數指針:pfun(實參...) 或者(*pfun)(實參...)。
現看一個實例:
- #include?<stdio.h>??
- int?fun0(const?int?&a)//定義謂詞1?(我們把作為函數參數的函數稱為謂詞)??
- {??
- ????if(a==1)??
- ????????return?1;??
- ????else??
- ????????return?0;??
- }??
- int?fun1(const?int?&a)//定義謂詞2??
- {??
- ????if(a<10)??
- ????????return?1;??
- ????else??
- ????????return?0;??
- }??
- /*根據whatCondition函數指針所指的函數(謂詞)設置的條件打印數組*/??
- void?print_arr_if(const?int?*arr,?int?alen,?int(*whatCondition)(const?int&))??
- {??
- ????for(inti=0;?i<alen;?i++)??
- ????{??
- ????????if(?whatCondition(arr[i])?)??
- ????????????printf("%d?",arr[i]);??
- ????}??
- ????printf("\n");??
- }??
- /**?
- Author:花心龜?
- Blog:http://blog.csdn.net/zhanxinhang?
- **/??
- int?main()??
- {????
- ????int?arr[]={1,1,2,1,11,12,3,10};??
- ????print_arr_if(arr,sizeof(arr)/sizeof(int),fun0);?//打印結果為1?1?1??
- ????print_arr_if(arr,sizeof(arr)/sizeof(int),fun1);?//打印結果為1,1,2,1,3??
- ???
- ??????
- ????return?0;??
- }??
小結:以上利用whatCondition函數指針大大提高了print_arr_if函數的擴展性,通過對謂詞的定義幾乎可打印你想要的任何數組元素。
?
魅力4 函數傳遞——高效,實用
c/c++有三種函數傳遞方式,它們分別是值傳遞,指針傳遞和引用傳遞,其中指針傳遞與引用傳遞都是將地址傳進函數,基本上沒什么區別。
指針及引用作為函數傳遞類型,與值傳遞相比,是高效的。為什么高效呢?請看如下:
現有如下結構體:
- typedef?struct?structType??
- {??
- int?i;??
- char?arr[100];??
- }structType;??
- //一個print函數的定義:??
- void?print0(const?structType?data)??
- {??
- ???????//printf?something?about?data??
- }??
- //另一個print函數的定義:??
- void?print1(const?structType?*pdata)??
- {??
- ???????//printf?something?about?data??
- }??
通過比較發現,print1比print0有明顯的效率優勢,因為print0是值傳遞,當值傳進去時,必須要開辟一個structType那么大的內存空間來乘裝這些值,這就要相當大的一部分資源消耗,而print1是指針傳遞,傳進去的是地址,一個地址只需4字節內存空間,使用時解析其指針即可,因此它比print0更高效更實用。
再看我們如何用一個函數交換兩個變量的值:
- swap(int?&a,?int?&b)??
- {??
- ?????int?temp?=?a;??
- ?????a=?b?;??
- ?????a?=?temp;??
- }??
交換函數只有使用引用傳遞或指針傳遞才能改變形參a和b所指向的內存的值。
小結:函數的指針傳遞與引用傳遞提供了函數傳遞的一個高效的途徑,另外,若要使用某個函數來改變某一變量的值唯有使用指針傳遞或引用傳遞給該函數,這體現了實用性。
魅力5 鏈——實用
在鏈表,二叉樹等數據結構中,指針作為鏈接兩個節點的“鏈”,時刻牽絆著它們的邏輯關系。指針為這些數據結構提供了高效實現的可能,因此使我們能夠在內存世界里完成對自然事物的邏輯構造,使我們的計算機更具實用價值。
?
魅力6 動態內存分配——實用
動態內存分配離不開指針,假如把內存空間當做容器,每個程序都有它自己的容器,它如同一個容器補給器,可隨時搭載從外界而來的一個個容器,隨時為您排憂解難,為程序分憂。不僅如此,你可以通過該補給器將申請而來的容器送回去,讓它為別地應用程序所利用。
?
……
另:如何善用指針
首先看什么是野指針和空指針
空指針: 如 int *p = NULL 這就定義了一個指針,通常NULL是一個零值,操作系統定義內存64kb以下的內存單元是不可訪問的,所以像如 *p = 9 這樣給他賦值是系統不允許的,將會發生內存報錯。
野指針: 如 int *p就是一個野指針,可以看到它在創建時沒有賦初值,所以它的值是一個隨機數,也就是亂指一氣,通常都是指向了不合法的內存段,所以使用它也會內存報錯。還有指針p被free或者delete之后也會成為野指針,因為它所指的內存空間被釋放之后,變成了一個不合法內存段。野指針,它顧名思義它就是一個野指針,它是沒有主人領養的野獸,兇猛殘暴,用它你就得自食其果。
指針防災措施:一,養成好習慣,在每聲明一個指針時便對它賦初值NULL。二、養成好習慣,在指針被free或者delete之后,對其賦值為NULL。三、在一定做好一和二的基礎下,就可以在使用指針之前只用if語句判斷指針是否為空,以做到防錯。四、避免強制類型轉換指針,除非能確保轉換前后類型所占用的空間大小是一致的。詳情可看此文:http://blog.csdn.net/zhanxinhang/article/details/6719387。
總結:指針可以當做一很好的工具,只要我們好好理解它善用它,它就能發揮它所具有的魅力。也許要真正理解指針需要些許時間去磨練去思考,但是有付出總有回報,回報過后都是值得的。現如今由于java,c#等語言的盛行,似乎指針的用武之地變少了。那么是不是有了java,c#等理解指針就不重要了呢?非也,非也。我從高中就開始接觸編程,接觸指針,上到大學才開始接觸java,c#等,從中我發現這些語言沒了指針確實是會少了些煩惱,尤其是對初手。但,對指針的理解(注意指針與內存息息相關)有助于您了解java等的底層世界,如垃圾回收機制,java虛擬機等,我認為了解這些對一個走專業化道路的java程序員是必須的。多了解點底層,多一點自由,少一點束縛。雖然要說讓指針發揮得像鋤頭那樣的驚人魅力,有點大,但至少在計算機世界里,它是的,因為有了它才有了像java這樣神奇的東西。
?