C語言中級教程再談指針8_第1頁
C語言中級教程再談指針8_第2頁
C語言中級教程再談指針8_第3頁
C語言中級教程再談指針8_第4頁
C語言中級教程再談指針8_第5頁
已閱讀5頁,還剩92頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

1、C語言中級培訓八、再談指針先說地址的概念內存地址:物理內存中存儲單元的編號。使用內存:在地址所標識的存儲單元中存放數(shù)據(jù)。注意:內存單元的地址與內存單元中的數(shù)據(jù)是兩個完全不同的概念。一個是address;另一個是value;訪問方式:(1)直接訪問使用變量名進行存取(2)間接訪問通過該變量的地址來訪問addressnamevalue指針的概念是一種特殊形式的變量。特在哪兒? 其value 是別人的地址、 類型不用于指導開辟空間、 可以無類型、 間址訪問才有意義、 行為受限。addressnamevalue指針和地址指針是一種容納地址的變量,通常也叫指針變量,統(tǒng)稱指針。而地址則是內存單元的編號,是

2、值。指針絕對不應等同于地址,千萬不要把二者意義給混淆了。 實際上指針是數(shù)據(jù)類型的一種,它應該跟整型、字符 型,浮點型是一樣的,只不過是組合類型罷了 。 指針變量只是存儲地址類型的變量,它不是數(shù)據(jù)類型。 指針能夠進行加減法,原因并不是因為它是指針。加減法不是指針變量的專利,而是地址這種數(shù)據(jù)類型的本能,正是因為地址具有加減的能力,所以才使指針作為存放地址的變量能夠進行加減運算 。指針與變量的關系指針變量和它所指向的變量之間的關系,用指針運算符“*”表示的。 “間接訪問”、“間址運算”例如,指針變量num_pointer與它所指向的變量num的關系,表示為:*num_pointer,即*num_po

3、inter等價于變量num。因此,下面兩個語句的作用相同:num=3; /*將3直接賦給變量num*/num_pointer=# /使num_pointer指向num *num_pointer=3;/將3賦給指針所指向的變量一般形式:存儲類型 數(shù)據(jù)類型 *指針變量名=初始地址值;例 void main( ) int i; static int *p=&i; / ( ) . 不能用auto變量的地址去初始化static型指針Why ?指針的初始化指針的類型對于 int * p = &i ;語句的正確理解是:定義了一個類型為 int * 的指針。此時不要將 int 和 * 分開,因為 in

4、t *是個新類型,是組合類型。許多人錯誤地認為這是定義了一個整型( int) 的指針(*p)。這恰是將“定義了一個指針”,還是“對p作求值運算”混淆的根源。typedef int * IntPtr;IntPtr pa,pb,pc ;typedef int * IntPtrPtr;IntPtrPtr ppa,ppb,ppc ;typedef char * CharPtr;CharPtr pCh1 , pCh2;typedef void * VoidPtr;VoidPtr pVoid;指針的賦值僅有5種形式:p = &a;p = A;p = q;p = f;p = NULL;指針是最神通廣大的“特

5、權階層” ,它不受棧的約束,可以訪問棧存儲區(qū)、靜態(tài)存儲區(qū)、堆存儲區(qū),甚至代碼區(qū)。最能體現(xiàn)C的靈活性,也為C程序埋下了隱患。指針與數(shù)組的區(qū)別:char str = “hello”;char *p = str;int n = 10;在32位計算機上,計算sizeof的值:sizeof (str) = ?sizeof (p) = ?sizeof (n) = ?void Fun(char str100) sizeof (str) = ?指針變量的零值是“空”(記為NULL)。盡管NULL的值與0相同,但是兩者意義不同。假設指針變量的名字為p,它與零值比較的標準if語句如下:if (p = NULL)/

6、 p與NULL比較,強調p是指針變量或 if (p != NULL) 不要寫成if (p = 0) / 容易讓人誤解p是整型變量if (p != 0) 或: if (p)/ 容易讓人誤解p是布爾變量 if (!p)指針變量與零值比較:有時候我們可能會看到 if (NULL = p) 這是寫錯了嗎? 不!是有意把p和NULL的位置顛倒。因為當把if (p = NULL) 誤寫成 if (p = NULL)時,編譯器會認為 if (p = NULL) 是合法的,它將執(zhí)行一條賦值語句,然后將表達式的值作為if的判斷依據(jù)。顯然那不是我們的本意。但若不小心把 if ( NULL = p) 寫成 if (

7、 NULL = p),編譯器會指出語法錯誤,因為NULL不能被賦值。這樣可以避免不易察覺的錯誤的發(fā)生,讓編譯器替我們排除更多的錯誤。指針的漏洞VC+6.0竟然允許這樣使用指針:#include stdio.hvoid main()int a = 1234; / 0 x0012ff7c是&a的值。int *p =( (int*)0 x0012ff7c );printf(&a = %xn,&a);printf(a = %dn*p = %dn,a,*p);關于“野指針”“野指針”是未指向具體變量的指針 (又稱指針懸空,此時它會亂指一氣)。 “野指針”產(chǎn)生的3種原因:指針變量沒有被初始化。指針變量剛被

8、創(chuàng)建時不會自動成為NULL指針,它的默認值是隨機的;指針p被free或者delete之后,沒有置為NULL,讓人誤以為p是個合法的指針;指針操作超越了變量的作用范圍;關于“野指針”避免“野指針”產(chǎn)生的對策:使用指針前一定要保證它指向了有效的內存空間(或者申請,或者讓指針指向一塊合法的空間)用malloc或new申請內存之后,應該立即檢查指針值是否為NULL。不要忘記為數(shù)組和動態(tài)內存賦初值。防止將未被初始化的內存作為右值使用。避免數(shù)組或指針的下標越界,特別要當心發(fā)生“多1”或者“少1”操作。動態(tài)內存的申請與釋放必須配對,防止內存泄漏。用free或delete釋放了內存之后,立即將指針設置為NUL

9、L,防止產(chǎn)生“野指針”。指針的類型轉換大存儲類型的指針向小存儲類型的指針轉換,會帶來錯誤風險 如 int a 0 x12345678; int *pa = &a; char *pc = (char*)pa; (*pa)的數(shù)值為0 x12345678; (*pc)的數(shù)值為0 x12;小存儲類型的指針向大存儲類型的指針轉換,會帶來極大的錯誤風險 如 char b 0 x12; char *pb = &b; int *pd = (int*)pb; *pb的數(shù)值為0 x12,*pd 的數(shù)值未知;paa(4byte)0 x120 x340 x560 x78pcb(1byte)0 x12未知未知未知pdp

10、b什么是數(shù)組名數(shù)組名是一個地址,而且還是一個不可修改的常地址。完整的說,數(shù)組名就是一個地址常量。數(shù)組名這個符號代表了數(shù)組內存的首地址。 注意:不是這個符號的值是首地址,是這個符號本身就代表一個地址。由于數(shù)組名是一個符號常量,因此它只能是個右值, 而指針作為變量是個左值,數(shù)組名永遠都不會是一個指針變量。C語言中對于多維數(shù)組的實現(xiàn)C語言只能用數(shù)組的數(shù)組當作多維數(shù)組。數(shù)組的數(shù)組與多維數(shù)組的主要區(qū)別,就在于數(shù)組的數(shù)組 各維之間的內在關系是一種鮮明的層級關系。上一維把下一維看作下一級數(shù)組,也就是數(shù)組嵌套。類型是層次間聯(lián)系的紐帶。數(shù)組引用時需要層層解析,直到最后一維。 A453 多維數(shù)組外觀A002A01

11、2A022A032A042A102A112A122A132A142A202A212A222A232A242A302A312A322A332A342A001A011A021A031A041A101A111A121A131A141A201A211A221A231A241A301A311A321A331A341A000A010A020A030A040A100A110A120A130A140A200A210A220A230A240A300A310A320A330A3400A12300010203041011121314202122232430313233340000010020100110120200

12、21022030031032040041042實際存儲的格式指針與數(shù)組永恒的公式:間址(首址 + 偏移量)“ 指針即含有一維數(shù)組!”與一維數(shù)組的關系:指向一維數(shù)組的指針。與N維數(shù)組的關系:指向N-1維數(shù)組的指針。訪問數(shù)組元素a0a1a2a3a9.aa+9a+1a+2地址元素下標法a0a1a2a9a0a1a2a3a9.pp+9p+1p+2地址元素指針法*p*(p+1)*(p+2)*(p+9)ai *(a+i) *(p+i) pi *a*(a+1)*(a+2)*(a+9)計算法p0p1p2p9指針下標法三種方法的統(tǒng)一:三種方法的本質都是:間址(變址)無論是下標法(a i )還是計算法(*(a+i)

13、)還是指針法(* (p +i) ),盡管表現(xiàn)形式不同,可本質都是: *(首址+ 偏移量)與指針相關的運算符下標運算符 則是 *(首址 + 偏移量 )。其中含有四重運算:先乘,再加,變址后,再間址。括號提高了運算優(yōu)先級。因a是二維數(shù)組名,代表二維數(shù)組的首地址,不能用*a來獲得a00的值,*a只相當于a0;正確的訪問形式是: int i,a 3 4 = 1,2 , 3,4,5 , 6,7,8,9 ; int (*p)4 = a;/p是指向二維數(shù)組的指針;p本身就包含一個維數(shù)組。 i=a01; 下標法或 i=*(*(a+0)+1); 計算法或 i=*(*(p+0)+1); 指針法或 i=*(a0+1

14、); 下標計算法或 i=*(a+0)1; 計算下標法或 i=*(p+0)1; 指針計算下標法或 i=*(p0+1); 指針下標計算法對二維數(shù)組元素的訪問:語言規(guī)定:數(shù)組名代表了數(shù)組的地址,所以ai是第i行那個一維數(shù)組的地址, 它指向該行的第0列元素。 ai可視為“一個一維數(shù)組的列指針”。同理, a可視為“一個以一維數(shù)組為大元素的一維數(shù)組的指針”。以此作為遞歸單元,即可推衍出N維數(shù)組。對于例子 :int A453; 定位一個具體的值, 如 A341 ,只要 *(*(*( A+3)+4)+1)C語言的數(shù)組實現(xiàn)并非真正的多維數(shù)組,而是數(shù)組嵌套,訪問某個元素的時候,需要逐層向下解析。 第一維元素A0

15、是A0所代表的那個數(shù)組的首地址,但是這個表達式在C 里面有特殊意義。 特殊之處在于它所代表的東西與一般的地址不同。而且類型也不是一般的地址類型,叫數(shù)組類型。當遇到 運算符的時候,編譯器只是簡單地把它轉換為類似*(*(A+i)+j)這樣的等價表達式 。 例如 AB 等價于 *(A+B).對于 例如 &AB的操作 ,編譯器將直接將其優(yōu)化成A+B.若有 int i = 10; int * p = & i ; & * p 意味著什么? * & i 又意味著什么? p i 最主要的區(qū)別就是長度不同 一般的地址長度為4 (不同的系統(tǒng)可能會不一樣) 數(shù)組類型的長度代表的數(shù)組的長度。 如在int A53;中

16、sizeof( A0 ) =5*3*sizeof(int)數(shù)組類型跟一般地址類型的區(qū)別數(shù)組類型在數(shù)組的定義與引用中具有非常重要的作用,它可以用來識別一個標識符或表達式是否真正的數(shù)組。一個真正數(shù)組的數(shù)組名,是一個具有數(shù)組類型的地址常量,它的長度,是整個數(shù)組的長度,并非一個一般地址的長度,如果一個標識符不具備數(shù)組類型,它就不是一個真正的數(shù)組。&a000僅僅是一個地址,它的意義,僅僅表示元素a000的地址,sizeof(&a000)的結果是4。不少人把它說成是數(shù)組a的首地址,這是錯誤的,這是對數(shù)組首地址概念的濫用。真正能代表數(shù)組a的數(shù)組首地址只有a本身,a與&a000的意義根本就是兩回事,真正的數(shù)組

17、首地址是具有數(shù)組類型的地址,sizeof(a)結果是ixjxkxsizeof(int),而不是4,只不過由于a000位置特殊,恰好是數(shù)組a的第一個元素,所以它們的地址值才相同。 雖然指針和數(shù)組都表示地址,有時可以混用、相互替換。但它們是有區(qū)別的。指針指向的是內存塊,可以改變;而數(shù)組名是某塊固定內存的地址,不能改變。如:char a10, *p; p = a; /OK char a10, b10; a = b; / error通過數(shù)組名可以求得數(shù)據(jù)塊的大小,通過指針則不能int a10;int *p;p = a;printf( “%d”, sizeof( a );/* output is 40

18、*/printf( “%d”, sizeof( p );/* output is 4 */對于指針和數(shù)組的區(qū)別還表現(xiàn)在使用scanf和printf上:void main() char name14,*p=name; scanf(“%s”, &name); scanf(“%s”, name); scanf(“%s”, p); scanf(“%s”, &p); printf(%sn, &name); printf(%sn, name); printf(%sn, p); printf(%sn, &p);但對于指針則一定不能帶對于數(shù)組名可以帶&也可以不帶但對于指針則一定不能帶對于數(shù)組名可以帶&也可以不

19、帶當數(shù)組作為函數(shù)的參數(shù)進行傳遞時,該數(shù)組自動蛻化為同類型的指針。因為當初ANSI委員會制定標準的時候,從C程序的執(zhí)行效率出發(fā),不主張參數(shù)傳遞時復制整個數(shù)組,而是傳遞數(shù)組的首地址,由被調函數(shù)根據(jù)這個首地址處理數(shù)組中的內容。那么誰能承擔這種“轉換”呢?這個主體必須具有地址數(shù)據(jù)類型,同時應該是一個變量,滿足這兩個條件的,當然非指針莫屬了 。通常,對于int a89這個二維數(shù)組,我們可以定義一個指向它的指針:int (*p)9;指向二維數(shù)組指針變量說明的一般形式為: 類型說明符 (*指針變量名)長度 其中“類型說明符”為所指數(shù)組的數(shù)據(jù)類型?!?”表示其后的變量是指針類型?!伴L度”表示二維數(shù)組分解為多個

20、一維數(shù)組時, 一維數(shù)組的長度,也就是二維數(shù)組的列數(shù)。 思考一下: int (*p)9 和 int *p9 什么區(qū)別?前者一個指針;后者九個指針。void main()static int a34=0,1,2,3,4,5,6,7,8,9,10,11;int(*p)4;int i,j;p=a;for(i=0;i3;i+) for(j=0;j4;j+) printf(%2d ,*(*(p+i)+j); 類似的,對于 一個指向N維數(shù)組的指針可以這樣定義: 類型說明符 (*指針變量名)長度長度長度 int (*p)x2x3.xn; 一個例子:int (* ptr)3; ptr=new int23;再談指

21、針與數(shù)組讀這段代碼,給出輸出結果,并解釋為什么?#include void main()int a5=1,2,3,4,5;int *ptr=(int *)(&a+1); / 請注意&a的含義!加一后指針指到數(shù)組的后面了。/printf(“%p,%pn”,&a+1,ptr); / 將地址顯示出來printf(%d,%dn,*(a+1),*(ptr-1);輸出:2,5解釋:*(a+1)就是a1,*(ptr-1)就是a4,執(zhí)行結果是2,5 。對于int *ptr=(int *)(&a+1); 系統(tǒng)不認為&a+1是首地址+1。對數(shù)組名求址 (&a) ,會導致數(shù)組維數(shù)升級,變成了數(shù)組指針,其類型為 in

22、t (*)5。而這樣的指針加1,則要根據(jù)指針類型加上一個a數(shù)組的偏移,是偏移了整個數(shù)組的大?。ū纠?個int),所以要加5*sizeof(int) 。于是ptr實際是&(a5),也就是a+5了,這時ptr實際指向a5。但prt與(&a+1)類型是不同,所以prt-1只會減去sizeof(int*)。而a、&a的地址是一樣的,但意思不一樣,a是數(shù)組首地址,也就是a0的地址,&a是對象(數(shù)組)首地址,a+1是數(shù)組下一元素的地址,即a1,&a+1是下一個對象的地址,即a5.眾所周知,C語言是沒有字符串變量的,因而,C89規(guī)定,字符串常量就是一個字符數(shù)組。因此,盡管字符串常量的外部表現(xiàn)形式跟數(shù)組完全

23、不同,但它的確是一個真正的數(shù)組 . 字符串常量本身就是這個數(shù)組的首地址,并且具有數(shù)組類型,對一個字符串常量進行sizeof運算,例如sizeof(abcdefghi),結果是10,而不是4. 字符串數(shù)組與一般數(shù)組的區(qū)別字符串常量存放在靜態(tài)存儲區(qū),而一般數(shù)組(非static)則是在棧中靜態(tài)分配的。 由于字符串常量是數(shù)組首地址,因此可以數(shù)組引用的形式使用它,例如:printf(%s, &abcdefghi4);這將打印出字符串efghi。還可以這樣:printf(%s, abcdefghi+4);同樣打印出字符串efghi。實際上,&“abcdefghi”4等價于&*(“abcdefghi”+4)

24、,去掉&*后,就是abcdefghi+4了 字符串的問題【問題1】請問以下(1)和 (2)有區(qū)別嗎? (1) char str = “HelloWorld”; char *p = str; (2) char *p = “HelloWorld”;/p只讀。它指向了長串.應改為 const char *p=“HelloWorld”;【問題2】請問b和c的值各是多少? char a = “Hello”; int b, c; b = sizeof(a);/測占多大空間,6個。 c = strlen(a);/測有效字符個數(shù)5個。錯。因為指針指向的是常量區(qū),而p卻是個指向變量的指針,那意味著可改所指的數(shù)據(jù)

25、,故錯誤。缺const修飾。 對??梢酝ㄟ^數(shù)組名或指針去修改。例 有若干計算機圖書,請按字母順序,從小到大輸出書名。解題要求:使用排序函數(shù)完成排序,在主函數(shù)中進行輸入輸出。/*案例代碼文件名:AL9_12.C*/*程序功能:指針數(shù)組應用示例*/* sort()函數(shù):對字符指針數(shù)組進行排序 */*形參:name字符指針數(shù)組,count元素個數(shù)*/*返回值:無 */*/void sort(char *name, int count) char *temp_p; int i,j,min; /*使用選擇法排序*/ for(i=0; icount-1; i+) /*外循環(huán):控制選擇次數(shù)*/ min=i;

26、 /*預置本次最小串的位置*/ for(j=i+1; j0) /*若存在更小的串*/ min=j; /*則保存之*/ if(min!=i) /*存在更小的串,交換位置*/ temp_p=namei,namei=namemin,namemin=temp_p; main() /*主函數(shù)main()*/ char *name5= “BASIC”,”FORTRAN”,”PASCAL”,”C”,”FoxBASE”; int i=0; sort(name,5); /*使用數(shù)組名作實參,調用排序函數(shù)sort()*/ /*輸出排序結果*/ for(; i5; i+) printf(“%sn”,namei);

27、程序運行結果:BASICCFORTRANFoxBASEPASCAL程序說明:(1)實參對形參的值傳遞: sort( name , 5 ); void sort(char *name, int count);(2)字符串的比較只能使用strcmp()函數(shù)。形參字符指針數(shù)組name的每個元素,都是一個指向字符串的指針,所以有strcmp(namemin,namej)?!纠俊颁忼X數(shù)組”分配內存和釋放內存的過程樣例。#include #include void main()int a1 = 5;int a2 = 4,2,3,2,4;int i,j;int * * p_array;p_array =

28、(int*)calloc(a1,sizeof(int *); for(i=0; ia1; i+) p_arrayi = (int*)calloc(a2i,sizeof(int);for(i=0; ia1; i+) for(j=0; ja2i; j+)p_arrayij = 1+2*i*j; for(i=0; ia1; i+) for(j=0; ja2i; j+)printf(%d ,p_arrayij);printf(n);for(i=0; ia1; i+)free(p_arrayi);free(p_array); 為何這樣釋放?指針的算術運算可以進行的算術運算,只有以下幾種: pxn, px

29、+/+px, px-/-px, px-py pxn:將指針從當前位置向前(+n)或回退(-n)n個數(shù)據(jù)單位,而不是n個字節(jié)。顯然,px+/+px和px-/-px是pxn的特例(n=1)。 px-py:兩指針之間的數(shù)據(jù)個數(shù),而不是指針的地址之差。情況一:int * pi指針指向常量const int iconst int i=40;int *pi;pi=&i;/這樣可以嗎?不行,(常量不能交給變量。)在VC+下是編譯錯。const int 類型的i的地址是不能賦給指向int 類型地址的指針pi的。否則pi豈不是能修改i的值了嗎!pi=(int* ) &i;/這樣可以嗎?強制類型轉換可是C語言所支

30、持的。VC+下編譯通過,但是仍不能通過*pi=80來修改i的值。去試試吧!看看具體的結果。指針與常情況二:const int * pi指針指向const int iconst int i=40;const int * pi;pi=&i;/兩個類型相同,可以這樣賦值。很顯然,i的值無論是通過pi,還是i都不能修改。 情況三:用const int * const pi聲明的指針 int i; const int * const pi=&i; 你能想像pi能夠作什么操作嗎?pi值不能改,也不能通過pi修改i的值。因為不管是*pi還是pi都是const的.指針與函數(shù)指針出現(xiàn)在函數(shù)的三個地方:返回類型

31、函數(shù)名(形參表); 合起來叫函數(shù)類型這叫返回類型這叫參數(shù)類型可以依此證實:typedef float (*pf)(int, double); pf 是個類型名指針與函數(shù)參數(shù)函數(shù)的形參表中可以含有指針。有多種形式:指針作形參,如void f(int *p); 作用是對作為變量的實參傳址,如 f ( &a );指向指針的指針作形參,如void f(int *pp); 作用是對作為指針的實參傳址,如 f ( &p );指針的引用作形參,如void f(int *&rp); 作用是對作為指針的實參傳址,如 f ( p );指針變量,既可以作為函數(shù)的形參,也可以作函數(shù)的實參。指針變量作實參時,與普通變量

32、一樣,也是“值傳遞”,即將指針變量的值(一個地址)傳遞給被調用函數(shù)的形參(必須是一個指針變量)。請注意:被調用函數(shù)可以改變實參指針變量所指向的變量的值。也可以改變形參的值,但對于實參指針變量沒有影響改了也白改。void Allocate( char * & p , int size)p = (char*) malloc(size);void Test (void ) char *str = NULL; Allocate ( str , 100 ) ; strcpy ( str , “Hello World!” ); printf ( str ) ; free ( str );此處改為char

33、* p 行嗎?改為char * p 呢?用字符指針變量作形參:void copy_string(char *from,char *to) /指針作形參for (;*from!=0;from+,to+)*to=*from;*to=0;main()char *a=“I am a teacher.”; char *b=“You are a student.”;printf(“nstring a=%snstring b=%sn”,a,b);copy_string(a,b);printf(“nstring a=%snstring b=%sn”,a,b) void copy_string(char *fr

34、om,char *to) while (*to=*from)!=0) from+;to+ void copy_string(char *from,char *to) while (*to+=*from+)!=0); void copy_string(char *from,char *to) while (*to+=*from+); 在以往的程序中,主函數(shù)main()都使用無參形式。實際上,主函數(shù)main()也是可以指定形參的。主函數(shù)main()的有參形式: main(int argc, char *argv) 實參的來源:運行帶形參的主函數(shù),必須在操作系統(tǒng)狀態(tài)下,輸入主函數(shù)所在的可執(zhí)行文件名,

35、以及所需的實參,然后回車即可。命令行的一般格式為: 可執(zhí)行文件名實參 實參2本案例程序的用法:lock +|- 主函數(shù)main()的形參案例9.13 用同一程序實現(xiàn)文件的加密和解密。約定:程序的可執(zhí)行文件名為lock.exe;其用法為:lock +|- 其中“+”為加密,“-”為解密。/*案例代碼文件名:AL9_13.C*/*程序功能:帶參主函數(shù)的應用示例*/main(int argc, char *argv) char c; if (argc != 3) printf(參數(shù)個數(shù)不對!n); else c=*argv1;/*截取第二個實參字符串的第一個字符*/ switch(c) case +

36、: /*執(zhí)行加密*/ /*加密程序段*/ printf(執(zhí)行加密程序段。n); break; case -: /*執(zhí)行解密*/ /*解密程序段*/ printf(執(zhí)行解密程序段。n); break; default: printf(第二個參數(shù)錯誤!n); 形參說明(1)形參argc是命令行中參數(shù)的個數(shù)(可執(zhí)行文件名本身也算一個)。在本案例中,形參argc的值為3 (lock、+|-、文件名)。(2)形參argv是一個字符指針數(shù)組,即形參argv首先是一個數(shù)組(元素個數(shù)為形參argc的值),其元素值都是指向實參字符串的指針。在本例中,元素argv0指向第1個實參字符串“l(fā)ock”,元素argv1

37、 指向第2個實參字符串“+|-”,元素argv2指向第3個實參字符串“被處理的文件名”。函數(shù)以及函數(shù)名函數(shù)的定義functiontype functionname( argument list) function body函數(shù)名稱函數(shù)名稱是一個常量(不能修改)。利用函數(shù)名稱可以訪問(調用)函數(shù);那么我們可以定義函數(shù)指針,利用函數(shù)指針來完成對函數(shù)調用。指向函數(shù)的指針一個通常的函數(shù)調用的例子:void MyFun(int x);/此處的聲明也可寫成:void MyFun( int );void main(int argc, char* argv)MyFun(10);/這里是調用MyFun(10);

38、函數(shù)void MyFun(int x)/這里定義一個MyFun函數(shù)printf(“%dn”,x);從功能上或者說從數(shù)學意義上理解函數(shù),MyFun函數(shù)名代表的是一個功能(或是說一段代碼)。就象某一數(shù)據(jù)變量的內存地址可以存儲在相應的指針變量中一樣,函數(shù)的首地址也以存儲在某個指針變量里。這樣,就可以通過這個函數(shù)指針變量來調用所指向的函數(shù)了。在C系列語言中,任何一個變量,總是要先聲明,之后才能使用的。那么,函數(shù)指針變量也應該先聲明。以上面的例子為例,來聲明一個可以指向MyFun函數(shù)的函數(shù)指針變量FunP:void (*FunP)(int) ;也可寫成void (*FunP)(int x);函數(shù)指針變量

39、的聲明格式如同函數(shù)MyFun的聲明一樣,只不過把MyFun改成(*FunP)而已。這樣就有了一個能指向MyFun函數(shù)的指針了。當然,這個指針變量也可以指向其它所有具有相同函數(shù)格式的函數(shù)。函數(shù)指針變量的聲明有了FunP指針變量后,我們就可以對它賦值,指向MyFun,然后通過FunP來調用MyFun函數(shù)了。void MyFun(int x);void (*FunP)(int );/也可聲明成void(*FunP)(int x),但習慣上一般不這樣。void main(int argc, char* argv)MyFun(10);/這是直接調用MyFun函數(shù)FunP=MyFun;/對函數(shù)指針賦值(*

40、FunP)(20);/通過函數(shù)指針FunP來調用MyFun函數(shù) / FunP(20);/也可以寫成這樣通過函數(shù)指針變量調用函數(shù)void MyFun(int x)/定義了MyFun函數(shù)printf(“%dn”,x);MyFun與FunP的類型是相吻合的:type *與type *的關系。函數(shù)MyFun好像是一個int的數(shù)組名(常量),而FunP則是一個int *的指針變量。int A10,*pi;pi = A;/與FunP=MyFun比較。你的感覺呢?繼續(xù)看以下幾種情況:(這些可都是可以正確運行的代碼!)void main(int argc, char* argv)MyFun(10);/調用My

41、Fun(10);函數(shù)FunP=MyFun;/將函數(shù)地址賦給指針變量(*MyFun)(10);/函數(shù)名MyFun也可以有這樣的調用格式,就如同用(*FunP)(20);來調用MyFun函數(shù)真的是可以這樣的!依據(jù)以往的知識和經(jīng)驗來推理本篇的“新發(fā)現(xiàn)”,必定會推斷出以下的結論:1. MyFun的函數(shù)名與FunP函數(shù)指針都是一樣的,即都是函數(shù)指針。MyFun函數(shù)名是一個函數(shù)指針常量,而FunP是一個函數(shù)數(shù)指針變量,這是它們的關系。2. 但函數(shù)名調用如果都得如(*MyFun)(10);這樣,那書寫與讀起來都是不方便和不習慣的。所以C語言的設計者們才會設計成可允許MyFun(10);這種形式地調用(這樣方

42、便多了并與數(shù)學中的函數(shù)形式一樣)。3. 為統(tǒng)一起見,F(xiàn)unP函數(shù)指針變量也可以FunP(10)的形式來調用。4. 賦值時,即可FunP=&MyFun形式,也可FunP=MyFun。 定義函數(shù)的指針類型:就像自定義數(shù)據(jù)類型一樣,我們也可以先定義一個函數(shù)指針類型,然后再用這個類型來聲明函數(shù)指針變量。先給你一個自定義數(shù)據(jù)類型的例子。typedef int* PINT;/為int* 類型定義了一個PINT的 別名void main()int x;PINT px=&x;/與int * px=&x;是等價的。PINT類型 其實就是int * 類型.*px=10; /px就是int*類型的變量.下面我們來看

43、一下函數(shù)指針類型的定義及使用:(請與上對照?。﹙oid MyFun(int x);/此處的聲明也可寫成: void MyFun( int );typedef void (*FunType)(int );/這樣只是定義一個函數(shù)指針類型FunType FunP;/然后就可以用FunType類型來聲明FunP變量void main(int argc, char* argv) FunType FunP;/聲明了函數(shù)指針變量 MyFun(10); FunP=&MyFun; (*FunP)(20);void MyFun(int x)printf(“%dn”,x);首先,在void (*FunType)(i

44、nt ); 前加了一個typedef 。這只是定義一個名為FunType的函數(shù)指針類型,而不是FunType變量。然后使用FunType FunP;這句就如PINT px;一樣地聲明了一個FunP變量。其它相同。整個程序完成了相同的事。這樣做法的好處是:有了FunType類型后,我們就可以同樣地、很方便地用FunType類型來聲明多個同類型的函數(shù)指針變量了。如下:FunType FunP2,F(xiàn)unP3; 定義函數(shù)指針類型解析函數(shù)指針定義 typedef datatype ( *FuncPointerName) (argumentlist); FuncPointerName就是一個函數(shù)指針類型F

45、uncPointerName pfunc1, pfunc1; pfunc1和pfunc2就是兩個函數(shù)指針變量。把指針聲明成指向函數(shù)的指針int fun1(char*,int); int fun2(char*,int);int(*pfun1)(char*,int); / pfun1是指向函數(shù)的指針pfun1 = fun1; /給函數(shù)指針賦值int a = (*pfun1)(“abcdefg”, 7);/用指向函數(shù)的指針進行函數(shù)調用pfun1 = fun2; /指向函數(shù)的指針可以被修改int a = (*pfun1)(bcdefg, 6);各種指針 請說明各指針的含義:int *ptr; char

46、 *ptr;int *ptr;int *ptr3;int (*ptr)3int *ptr 34;int *(*ptr)4;void* (*ptr)(void*);int * pMove();int (*p3) (int);int (*p) 3 4;int (*p3) 4;一維指針數(shù)組指向二維指針數(shù)組的指針指向函數(shù)的指針返回指針的函數(shù)函數(shù)指針數(shù)組,函數(shù)返回int型數(shù)據(jù)二維指針數(shù)組指向二維數(shù)組的指針指向三維數(shù)組的指針指針數(shù)組的每個元素都是指向二維數(shù)組的指針復雜指針解析的方法分解法首先從未定義的標識符起往右看,當遇到圓括號時,就掉轉方向往左看。解析完圓括號里面所有的東西,就整個給它一個命名。將命名與

47、外面的東西再重復這個過程直到將整個聲明解析完畢。外面的東西其實是類型。 第一個例子 int (*func)(int *p);首先找到未定義的標識符,就是func,它的外面有一對圓括號,而且左邊是個*號,這說明func是個指針,然后跳出這個圓括號,并給它一個命名,比如F 。再看右邊,也是一個圓括號,這說明F是個函數(shù),而func是個指向這類函數(shù)的指針,這類函數(shù)具有int*類型的形參,返回值類型是int。 第二個例子 int (*func)(int *p, int (*f)(int*);func被一對括號包含,且左邊有個*號,說明func是個指針,給它一個命名,比如F。其右邊也有個括號,那么func

48、是個指向函數(shù)的指針,這類函數(shù)具有int *和int (*)(int*)形參,返回值為int類型。再來看形參int (*f)(int*),類似前面的解釋,f也是一個函數(shù)指針,指向的函數(shù)具有int*類型的形參,返回值為int 第三個例子 int (*func5)(int *p);func右邊是一個運算符,說明func是具有5個元素的數(shù)組,func的左邊有個*,說明func的數(shù)組元素是指針,要注意這里的*不是修飾func的,而是修飾func5的,原因是運算符優(yōu)先級比*高,func先跟5結合。給它一個命名,比如F 。看右邊,也是一對圓括號,說明func數(shù)組的元素是函數(shù)類型的指針,它所指向的函數(shù)具有in

49、t*類型的形參,返回值類型為int。所以func是“每個元素都是指向函數(shù)的指針的數(shù)組名”。 第四個例子 int (* (*func)5 )(int *p); func被圓括號包含,左邊又有*,那么func是個指針,右邊是個號,那么說明func是一個指向數(shù)組的指針,給它一個命名,比如F ,那么其余部分就是數(shù)組的類型了。往左看,有個*號,說明這個數(shù)組的元素是指針,跳出括號,右邊又有一對括號,說明是個形參。這說明數(shù)組的元素是指向函數(shù)的指針??偨Y一下,就是:func是一個指向二維數(shù)組的指針,這個數(shù)組的每個元素都是函數(shù)指針,這些指針指向具有int*形參,返回值為int類型的函數(shù)。 從以上解析可以看出,類

50、型是揭示謎底的法寶。 第五個例子 int (* (*func)(int *p) )5;func是一個函數(shù)指針,這類函數(shù)具有int*類型的形參(紅的部分)。返回值(黑的部分)是指向數(shù)組的指針,該指針所指向的數(shù)組的元素是具有5個int元素的數(shù)組 int (* )5 。 第六個例子 char (*(* fun () )4) (int *p) 從fun ()看fun是個函數(shù),將其命名為F,得: char (*(* F )4) (int *p) ;分析(* F )4,F(xiàn)是指向二維數(shù)組的指針,命名為P得char (*P) (int *p) ;可見P是個指向函數(shù)的指針。于是,fun是一個函數(shù),返回值是指向二

51、維數(shù)組的指針。所指向的數(shù)組的元素都是指針,每個指針都可以指向函數(shù)。這類函數(shù)具有返回char類型、形參是一個且類型是整型指針的架構。 第七個例子 int *(*(*func)(int *(*)10)10;func是一個指向函數(shù)指針,這類函數(shù)用指向二維的指針數(shù)組的指針做參數(shù),返回值的也是同類型指針。此句整體定義了一個指向函數(shù)的指針數(shù)組。若定義了typedef int * (*pa)10; 后,就可以用 pa(*func)(pa); 來代替該句。 練習解讀: char ( * ( * ( * q ) ( ) ) ) ( ); ( * q ) ( ) k 然后其它的東西都是它的類型 ( * k ) m

52、 這個數(shù)組的類型在下面 char ( * m ) ( ) 這是數(shù)組的類型總結:q是個指向函數(shù)的指針,該函數(shù)無形參,它的返回類型是指向二維數(shù)組的指針,數(shù)組元素的類型又是指向函數(shù)的指針,無形參,返回值是字符型。解讀1)int (*(*func)56)78;2)int (*(*(*func)(int *)5)(int *);3)int (*(*func789)(int*)5;1) func是指向三維數(shù)組的指針,這類數(shù)組的大元素是具有5X6個int元素的二維數(shù)組,而指向三維數(shù)組的指針又是另一個三維指針數(shù)組的元素。2) func是函數(shù)指針,這類函數(shù)的返回值是指向數(shù)組的指針,所指向數(shù)組的元素也是函數(shù)指針,

53、指向的函數(shù)具有int*形參,返回值為int。3) func是個三維指針數(shù)組,數(shù)組元素是函數(shù)指針,這類函數(shù)具有int*的形參,返回值是指向數(shù)組的指針,所指向的數(shù)組的元素是具有5個int元素的數(shù)組。指向指針的指針int i = 20;int *pi = &i;int *ppi = 指針變量ppi的內容就是指針變量pi的起始地址。于是ppi的值是多少呢?10000。是pi的址.*ppi的值是多少呢2006,即pi的值,也是i的址。*ppi的值是多少呢?20,即i的值,也是*pi的值。它可以指向指針,亦可以指向指針數(shù)組。設計一個函數(shù):void find1(char array, char search

54、, char * pa)要求:這個函數(shù)參數(shù)中的數(shù)組array是以0值為結束的字符串,要求在字符串a(chǎn)rray中查找與參數(shù)search給出的字符相同的字符。如果找到,通過第三個參數(shù)(pa)返回array字符串中首先碰到的字符的地址。如果沒找到,則為pa為0。依題意,實現(xiàn)代碼如下。指向指針的指針應用實例void find1(char array, char search, char * pa)int i;for (i=0;*(array+i)!=0;i+)if (*(array+i)=search)pa=array+ibreak;else if (*(array+i)=0)pa=0;break; 你

55、覺得這個函數(shù)能實現(xiàn)所要求的功能嗎?下面調用這個函數(shù)試試。void main()char str=“afsdfsdfdf”;/待查找的字符串char a=d;/設置要查找的字符char * p=0;find1(str,a,p);/調用函數(shù)以實現(xiàn)所要操作。if (0=p )printf (“沒找到!n”);/如果沒找到則輸出此句elseprintf(“找到了,p=%d”,p);/如果找到則輸出此句上面代碼,你認為會是輸出什么呢?明明a值為d,而str字符串的第四個字符就是d,應該找得到呀!分析:先看函數(shù)定義處:void find1(char array, char search, char * pa)再看調用處:find1(str,a,p);請仔細考慮此時形實結合所發(fā)生的事:array得到了數(shù)組名str,search得到了a的值, pa得到了p的值(而非p的地址)!可見盡管使用了指針,也并沒實現(xiàn)傳址,當實參形參都是指針時,它們也僅僅是傳值。畫出內存使用圖示就清楚了。修正:void find2(char array, char search, char * ppa)int i;for

溫馨提示

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

評論

0/150

提交評論