




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
1、C語言中級(jí)培訓(xùn)八、再談指針先說地址的概念內(nèi)存地址:物理內(nèi)存中存儲(chǔ)單元的編號(hào)。使用內(nèi)存:在地址所標(biāo)識(shí)的存儲(chǔ)單元中存放數(shù)據(jù)。注意:內(nèi)存單元的地址與內(nèi)存單元中的數(shù)據(jù)是兩個(gè)完全不同的概念。一個(gè)是address;另一個(gè)是value;訪問方式:(1)直接訪問使用變量名進(jìn)行存取(2)間接訪問通過該變量的地址來訪問addressnamevalue指針的概念是一種特殊形式的變量。特在哪兒? 其value 是別人的地址、 類型不用于指導(dǎo)開辟空間、 可以無類型、 間址訪問才有意義、 行為受限。addressnamevalue指針和地址指針是一種容納地址的變量,通常也叫指針變量,統(tǒng)稱指針。而地址則是內(nèi)存單元的編號(hào),是
2、值。指針絕對(duì)不應(yīng)等同于地址,千萬不要把二者意義給混淆了。 實(shí)際上指針是數(shù)據(jù)類型的一種,它應(yīng)該跟整型、字符 型,浮點(diǎn)型是一樣的,只不過是組合類型罷了 。 指針變量只是存儲(chǔ)地址類型的變量,它不是數(shù)據(jù)類型。 指針能夠進(jìn)行加減法,原因并不是因?yàn)樗侵羔?。加減法不是指針變量的專利,而是地址這種數(shù)據(jù)類型的本能,正是因?yàn)榈刂肪哂屑訙p的能力,所以才使指針作為存放地址的變量能夠進(jìn)行加減運(yùn)算 。指針與變量的關(guān)系指針變量和它所指向的變量之間的關(guān)系,用指針運(yùn)算符“*”表示的。 “間接訪問”、“間址運(yùn)算”例如,指針變量num_pointer與它所指向的變量num的關(guān)系,表示為:*num_pointer,即*num_po
3、inter等價(jià)于變量num。因此,下面兩個(gè)語句的作用相同:num=3; /*將3直接賦給變量num*/num_pointer=# /使num_pointer指向num *num_pointer=3;/將3賦給指針?biāo)赶虻淖兞恳话阈问剑捍鎯?chǔ)類型 數(shù)據(jù)類型 *指針變量名=初始地址值;例 void main( ) int i; static int *p=&i; / ( ) . 不能用auto變量的地址去初始化static型指針Why ?指針的初始化指針的類型對(duì)于 int * p = &i ;語句的正確理解是:定義了一個(gè)類型為 int * 的指針。此時(shí)不要將 int 和 * 分開,因?yàn)?in
4、t *是個(gè)新類型,是組合類型。許多人錯(cuò)誤地認(rèn)為這是定義了一個(gè)整型( int) 的指針(*p)。這恰是將“定義了一個(gè)指針”,還是“對(duì)p作求值運(yùn)算”混淆的根源。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、權(quán)階層” ,它不受棧的約束,可以訪問棧存儲(chǔ)區(qū)、靜態(tài)存儲(chǔ)區(qū)、堆存儲(chǔ)區(qū),甚至代碼區(qū)。最能體現(xiàn)C的靈活性,也為C程序埋下了隱患。指針與數(shù)組的區(qū)別:char str = “hello”;char *p = str;int n = 10;在32位計(jì)算機(jī)上,計(jì)算sizeof的值:sizeof (str) = ?sizeof (p) = ?sizeof (n) = ?void Fun(char str100) sizeof (str) = ?指針變量的零值是“空”(記為NULL)。盡管NULL的值與0相同,但是兩者意義不同。假設(shè)指針變量的名字為p,它與零值比較的標(biāo)準(zhǔn)if語句如下:if (p = NULL)/
6、 p與NULL比較,強(qiáng)調(diào)p是指針變量或 if (p != NULL) 不要寫成if (p = 0) / 容易讓人誤解p是整型變量if (p != 0) 或: if (p)/ 容易讓人誤解p是布爾變量 if (!p)指針變量與零值比較:有時(shí)候我們可能會(huì)看到 if (NULL = p) 這是寫錯(cuò)了嗎? 不!是有意把p和NULL的位置顛倒。因?yàn)楫?dāng)把if (p = NULL) 誤寫成 if (p = NULL)時(shí),編譯器會(huì)認(rèn)為 if (p = NULL) 是合法的,它將執(zhí)行一條賦值語句,然后將表達(dá)式的值作為if的判斷依據(jù)。顯然那不是我們的本意。但若不小心把 if ( NULL = p) 寫成 if (
7、 NULL = p),編譯器會(huì)指出語法錯(cuò)誤,因?yàn)镹ULL不能被賦值。這樣可以避免不易察覺的錯(cuò)誤的發(fā)生,讓編譯器替我們排除更多的錯(cuò)誤。指針的漏洞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);關(guān)于“野指針”“野指針”是未指向具體變量的指針 (又稱指針懸空,此時(shí)它會(huì)亂指一氣)。 “野指針”產(chǎn)生的3種原因:指針變量沒有被初始化。指針變量剛被
8、創(chuàng)建時(shí)不會(huì)自動(dòng)成為NULL指針,它的默認(rèn)值是隨機(jī)的;指針p被free或者delete之后,沒有置為NULL,讓人誤以為p是個(gè)合法的指針;指針操作超越了變量的作用范圍;關(guān)于“野指針”避免“野指針”產(chǎn)生的對(duì)策:使用指針前一定要保證它指向了有效的內(nèi)存空間(或者申請(qǐng),或者讓指針指向一塊合法的空間)用malloc或new申請(qǐng)內(nèi)存之后,應(yīng)該立即檢查指針值是否為NULL。不要忘記為數(shù)組和動(dòng)態(tài)內(nèi)存賦初值。防止將未被初始化的內(nèi)存作為右值使用。避免數(shù)組或指針的下標(biāo)越界,特別要當(dāng)心發(fā)生“多1”或者“少1”操作。動(dòng)態(tài)內(nèi)存的申請(qǐng)與釋放必須配對(duì),防止內(nèi)存泄漏。用free或delete釋放了內(nèi)存之后,立即將指針設(shè)置為NUL
9、L,防止產(chǎn)生“野指針”。指針的類型轉(zhuǎn)換大存儲(chǔ)類型的指針向小存儲(chǔ)類型的指針轉(zhuǎn)換,會(huì)帶來錯(cuò)誤風(fēng)險(xiǎn) 如 int a 0 x12345678; int *pa = &a; char *pc = (char*)pa; (*pa)的數(shù)值為0 x12345678; (*pc)的數(shù)值為0 x12;小存儲(chǔ)類型的指針向大存儲(chǔ)類型的指針轉(zhuǎn)換,會(huì)帶來極大的錯(cuò)誤風(fēng)險(xiǎn) 如 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ù)組名是一個(gè)地址,而且還是一個(gè)不可修改的常地址。完整的說,數(shù)組名就是一個(gè)地址常量。數(shù)組名這個(gè)符號(hào)代表了數(shù)組內(nèi)存的首地址。 注意:不是這個(gè)符號(hào)的值是首地址,是這個(gè)符號(hào)本身就代表一個(gè)地址。由于數(shù)組名是一個(gè)符號(hào)常量,因此它只能是個(gè)右值, 而指針作為變量是個(gè)左值,數(shù)組名永遠(yuǎn)都不會(huì)是一個(gè)指針變量。C語言中對(duì)于多維數(shù)組的實(shí)現(xiàn)C語言只能用數(shù)組的數(shù)組當(dāng)作多維數(shù)組。數(shù)組的數(shù)組與多維數(shù)組的主要區(qū)別,就在于數(shù)組的數(shù)組 各維之間的內(nèi)在關(guān)系是一種鮮明的層級(jí)關(guān)系。上一維把下一維看作下一級(jí)數(shù)組,也就是數(shù)組嵌套。類型是層次間聯(lián)系的紐帶。數(shù)組引用時(shí)需要層層解析,直到最后一維。 A453 多維數(shù)組外觀A002A01
11、2A022A032A042A102A112A122A132A142A202A212A222A232A242A302A312A322A332A342A001A011A021A031A041A101A111A121A131A141A201A211A221A231A241A301A311A321A331A341A000A010A020A030A040A100A110A120A130A140A200A210A220A230A240A300A310A320A330A3400A12300010203041011121314202122232430313233340000010020100110120200
12、21022030031032040041042實(shí)際存儲(chǔ)的格式指針與數(shù)組永恒的公式:間址(首址 + 偏移量)“ 指針即含有一維數(shù)組!”與一維數(shù)組的關(guān)系:指向一維數(shù)組的指針。與N維數(shù)組的關(guān)系:指向N-1維數(shù)組的指針。訪問數(shù)組元素a0a1a2a3a9.aa+9a+1a+2地址元素下標(biāo)法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)計(jì)算法p0p1p2p9指針下標(biāo)法三種方法的統(tǒng)一:三種方法的本質(zhì)都是:間址(變址)無論是下標(biāo)法(a i )還是計(jì)算法(*(a+i)
13、)還是指針法(* (p +i) ),盡管表現(xiàn)形式不同,可本質(zhì)都是: *(首址+ 偏移量)與指針相關(guān)的運(yùn)算符下標(biāo)運(yùn)算符 則是 *(首址 + 偏移量 )。其中含有四重運(yùn)算:先乘,再加,變址后,再間址。括號(hào)提高了運(yùn)算優(yōu)先級(jí)。因a是二維數(shù)組名,代表二維數(shù)組的首地址,不能用*a來獲得a00的值,*a只相當(dāng)于a0;正確的訪問形式是: int i,a 3 4 = 1,2 , 3,4,5 , 6,7,8,9 ; int (*p)4 = a;/p是指向二維數(shù)組的指針;p本身就包含一個(gè)維數(shù)組。 i=a01; 下標(biāo)法或 i=*(*(a+0)+1); 計(jì)算法或 i=*(*(p+0)+1); 指針法或 i=*(a0+1
14、); 下標(biāo)計(jì)算法或 i=*(a+0)1; 計(jì)算下標(biāo)法或 i=*(p+0)1; 指針計(jì)算下標(biāo)法或 i=*(p0+1); 指針下標(biāo)計(jì)算法對(duì)二維數(shù)組元素的訪問:語言規(guī)定:數(shù)組名代表了數(shù)組的地址,所以ai是第i行那個(gè)一維數(shù)組的地址, 它指向該行的第0列元素。 ai可視為“一個(gè)一維數(shù)組的列指針”。同理, a可視為“一個(gè)以一維數(shù)組為大元素的一維數(shù)組的指針”。以此作為遞歸單元,即可推衍出N維數(shù)組。對(duì)于例子 :int A453; 定位一個(gè)具體的值, 如 A341 ,只要 *(*(*( A+3)+4)+1)C語言的數(shù)組實(shí)現(xiàn)并非真正的多維數(shù)組,而是數(shù)組嵌套,訪問某個(gè)元素的時(shí)候,需要逐層向下解析。 第一維元素A0
15、是A0所代表的那個(gè)數(shù)組的首地址,但是這個(gè)表達(dá)式在C 里面有特殊意義。 特殊之處在于它所代表的東西與一般的地址不同。而且類型也不是一般的地址類型,叫數(shù)組類型。當(dāng)遇到 運(yùn)算符的時(shí)候,編譯器只是簡單地把它轉(zhuǎn)換為類似*(*(A+i)+j)這樣的等價(jià)表達(dá)式 。 例如 AB 等價(jià)于 *(A+B).對(duì)于 例如 &AB的操作 ,編譯器將直接將其優(yōu)化成A+B.若有 int i = 10; int * p = & i ; & * p 意味著什么? * & i 又意味著什么? p i 最主要的區(qū)別就是長度不同 一般的地址長度為4 (不同的系統(tǒng)可能會(huì)不一樣) 數(shù)組類型的長度代表的數(shù)組的長度。 如在int A53;中
16、sizeof( A0 ) =5*3*sizeof(int)數(shù)組類型跟一般地址類型的區(qū)別數(shù)組類型在數(shù)組的定義與引用中具有非常重要的作用,它可以用來識(shí)別一個(gè)標(biāo)識(shí)符或表達(dá)式是否真正的數(shù)組。一個(gè)真正數(shù)組的數(shù)組名,是一個(gè)具有數(shù)組類型的地址常量,它的長度,是整個(gè)數(shù)組的長度,并非一個(gè)一般地址的長度,如果一個(gè)標(biāo)識(shí)符不具備數(shù)組類型,它就不是一個(gè)真正的數(shù)組。&a000僅僅是一個(gè)地址,它的意義,僅僅表示元素a000的地址,sizeof(&a000)的結(jié)果是4。不少人把它說成是數(shù)組a的首地址,這是錯(cuò)誤的,這是對(duì)數(shù)組首地址概念的濫用。真正能代表數(shù)組a的數(shù)組首地址只有a本身,a與&a000的意義根本就是兩回事,真正的數(shù)組
17、首地址是具有數(shù)組類型的地址,sizeof(a)結(jié)果是ixjxkxsizeof(int),而不是4,只不過由于a000位置特殊,恰好是數(shù)組a的第一個(gè)元素,所以它們的地址值才相同。 雖然指針和數(shù)組都表示地址,有時(shí)可以混用、相互替換。但它們是有區(qū)別的。指針指向的是內(nèi)存塊,可以改變;而數(shù)組名是某塊固定內(nèi)存的地址,不能改變。如: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 */對(duì)于指針和數(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);但對(duì)于指針則一定不能帶對(duì)于數(shù)組名可以帶&也可以不帶但對(duì)于指針則一定不能帶對(duì)于數(shù)組名可以帶&也可以不
19、帶當(dāng)數(shù)組作為函數(shù)的參數(shù)進(jìn)行傳遞時(shí),該數(shù)組自動(dòng)蛻化為同類型的指針。因?yàn)楫?dāng)初ANSI委員會(huì)制定標(biāo)準(zhǔn)的時(shí)候,從C程序的執(zhí)行效率出發(fā),不主張參數(shù)傳遞時(shí)復(fù)制整個(gè)數(shù)組,而是傳遞數(shù)組的首地址,由被調(diào)函數(shù)根據(jù)這個(gè)首地址處理數(shù)組中的內(nèi)容。那么誰能承擔(dān)這種“轉(zhuǎn)換”呢?這個(gè)主體必須具有地址數(shù)據(jù)類型,同時(shí)應(yīng)該是一個(gè)變量,滿足這兩個(gè)條件的,當(dāng)然非指針莫屬了 。通常,對(duì)于int a89這個(gè)二維數(shù)組,我們可以定義一個(gè)指向它的指針:int (*p)9;指向二維數(shù)組指針變量說明的一般形式為: 類型說明符 (*指針變量名)長度 其中“類型說明符”為所指數(shù)組的數(shù)據(jù)類型。“*”表示其后的變量是指針類型?!伴L度”表示二維數(shù)組分解為多個(gè)
20、一維數(shù)組時(shí), 一維數(shù)組的長度,也就是二維數(shù)組的列數(shù)。 思考一下: int (*p)9 和 int *p9 什么區(qū)別?前者一個(gè)指針;后者九個(gè)指針。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); 類似的,對(duì)于 一個(gè)指向N維數(shù)組的指針可以這樣定義: 類型說明符 (*指針變量名)長度長度長度 int (*p)x2x3.xn; 一個(gè)例子:int (* ptr)3; ptr=new int23;再談指
21、針與數(shù)組讀這段代碼,給出輸出結(jié)果,并解釋為什么?#include void main()int a5=1,2,3,4,5;int *ptr=(int *)(&a+1); / 請(qǐng)注意&a的含義!加一后指針指到數(shù)組的后面了。/printf(“%p,%pn”,&a+1,ptr); / 將地址顯示出來printf(%d,%dn,*(a+1),*(ptr-1);輸出:2,5解釋:*(a+1)就是a1,*(ptr-1)就是a4,執(zhí)行結(jié)果是2,5 。對(duì)于int *ptr=(int *)(&a+1); 系統(tǒng)不認(rèn)為&a+1是首地址+1。對(duì)數(shù)組名求址 (&a) ,會(huì)導(dǎo)致數(shù)組維數(shù)升級(jí),變成了數(shù)組指針,其類型為 in
22、t (*)5。而這樣的指針加1,則要根據(jù)指針類型加上一個(gè)a數(shù)組的偏移,是偏移了整個(gè)數(shù)組的大?。ū纠?個(gè)int),所以要加5*sizeof(int) 。于是ptr實(shí)際是&(a5),也就是a+5了,這時(shí)ptr實(shí)際指向a5。但prt與(&a+1)類型是不同,所以prt-1只會(huì)減去sizeof(int*)。而a、&a的地址是一樣的,但意思不一樣,a是數(shù)組首地址,也就是a0的地址,&a是對(duì)象(數(shù)組)首地址,a+1是數(shù)組下一元素的地址,即a1,&a+1是下一個(gè)對(duì)象的地址,即a5.眾所周知,C語言是沒有字符串變量的,因而,C89規(guī)定,字符串常量就是一個(gè)字符數(shù)組。因此,盡管字符串常量的外部表現(xiàn)形式跟數(shù)組完全
23、不同,但它的確是一個(gè)真正的數(shù)組 . 字符串常量本身就是這個(gè)數(shù)組的首地址,并且具有數(shù)組類型,對(duì)一個(gè)字符串常量進(jìn)行sizeof運(yùn)算,例如sizeof(abcdefghi),結(jié)果是10,而不是4. 字符串?dāng)?shù)組與一般數(shù)組的區(qū)別字符串常量存放在靜態(tài)存儲(chǔ)區(qū),而一般數(shù)組(非static)則是在棧中靜態(tài)分配的。 由于字符串常量是數(shù)組首地址,因此可以數(shù)組引用的形式使用它,例如:printf(%s, &abcdefghi4);這將打印出字符串efghi。還可以這樣:printf(%s, abcdefghi+4);同樣打印出字符串efghi。實(shí)際上,&“abcdefghi”4等價(jià)于&*(“abcdefghi”+4)
24、,去掉&*后,就是abcdefghi+4了 字符串的問題【問題1】請(qǐng)問以下(1)和 (2)有區(qū)別嗎? (1) char str = “HelloWorld”; char *p = str; (2) char *p = “HelloWorld”;/p只讀。它指向了長串.應(yīng)改為 const char *p=“HelloWorld”;【問題2】請(qǐng)問b和c的值各是多少? char a = “Hello”; int b, c; b = sizeof(a);/測(cè)占多大空間,6個(gè)。 c = strlen(a);/測(cè)有效字符個(gè)數(shù)5個(gè)。錯(cuò)。因?yàn)橹羔樦赶虻氖浅A繀^(qū),而p卻是個(gè)指向變量的指針,那意味著可改所指的數(shù)據(jù)
25、,故錯(cuò)誤。缺const修飾。 對(duì)??梢酝ㄟ^數(shù)組名或指針去修改。例 有若干計(jì)算機(jī)圖書,請(qǐng)按字母順序,從小到大輸出書名。解題要求:使用排序函數(shù)完成排序,在主函數(shù)中進(jìn)行輸入輸出。/*案例代碼文件名:AL9_12.C*/*程序功能:指針數(shù)組應(yīng)用示例*/* sort()函數(shù):對(duì)字符指針數(shù)組進(jìn)行排序 */*形參:name字符指針數(shù)組,count元素個(gè)數(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、 /*預(yù)置本次最小串的位置*/ 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í)參,調(diào)用排序函數(shù)sort()*/ /*輸出排序結(jié)果*/ for(; i5; i+) printf(“%sn”,namei);
27、程序運(yùn)行結(jié)果:BASICCFORTRANFoxBASEPASCAL程序說明:(1)實(shí)參對(duì)形參的值傳遞: sort( name , 5 ); void sort(char *name, int count);(2)字符串的比較只能使用strcmp()函數(shù)。形參字符指針數(shù)組name的每個(gè)元素,都是一個(gè)指向字符串的指針,所以有strcmp(namemin,namej)?!纠俊颁忼X數(shù)組”分配內(nèi)存和釋放內(nèi)存的過程樣例。#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); 為何這樣釋放?指針的算術(shù)運(yùn)算可以進(jìn)行的算術(shù)運(yùn)算,只有以下幾種: pxn, px
29、+/+px, px-/-px, px-py pxn:將指針從當(dāng)前位置向前(+n)或回退(-n)n個(gè)數(shù)據(jù)單位,而不是n個(gè)字節(jié)。顯然,px+/+px和px-/-px是pxn的特例(n=1)。 px-py:兩指針之間的數(shù)據(jù)個(gè)數(shù),而不是指針的地址之差。情況一:int * pi指針指向常量const int iconst int i=40;int *pi;pi=&i;/這樣可以嗎?不行,(常量不能交給變量。)在VC+下是編譯錯(cuò)。const int 類型的i的地址是不能賦給指向int 類型地址的指針pi的。否則pi豈不是能修改i的值了嗎!pi=(int* ) &i;/這樣可以嗎?強(qiáng)制類型轉(zhuǎn)換可是C語言所支
30、持的。VC+下編譯通過,但是仍不能通過*pi=80來修改i的值。去試試吧!看看具體的結(jié)果。指針與常情況二:const int * pi指針指向const int iconst int i=40;const int * pi;pi=&i;/兩個(gè)類型相同,可以這樣賦值。很顯然,i的值無論是通過pi,還是i都不能修改。 情況三:用const int * const pi聲明的指針 int i; const int * const pi=&i; 你能想像pi能夠作什么操作嗎?pi值不能改,也不能通過pi修改i的值。因?yàn)椴还苁?pi還是pi都是const的.指針與函數(shù)指針出現(xiàn)在函數(shù)的三個(gè)地方:返回類型
31、函數(shù)名(形參表); 合起來叫函數(shù)類型這叫返回類型這叫參數(shù)類型可以依此證實(shí):typedef float (*pf)(int, double); pf 是個(gè)類型名指針與函數(shù)參數(shù)函數(shù)的形參表中可以含有指針。有多種形式:指針作形參,如void f(int *p); 作用是對(duì)作為變量的實(shí)參傳址,如 f ( &a );指向指針的指針作形參,如void f(int *pp); 作用是對(duì)作為指針的實(shí)參傳址,如 f ( &p );指針的引用作形參,如void f(int *&rp); 作用是對(duì)作為指針的實(shí)參傳址,如 f ( p );指針變量,既可以作為函數(shù)的形參,也可以作函數(shù)的實(shí)參。指針變量作實(shí)參時(shí),與普通變量
32、一樣,也是“值傳遞”,即將指針變量的值(一個(gè)地址)傳遞給被調(diào)用函數(shù)的形參(必須是一個(gè)指針變量)。請(qǐng)注意:被調(diào)用函數(shù)可以改變實(shí)參指針變量所指向的變量的值。也可以改變形參的值,但對(duì)于實(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í)際上,主函數(shù)main()也是可以指定形參的。主函數(shù)main()的有參形式: main(int argc, char *argv) 實(shí)參的來源:運(yùn)行帶形參的主函數(shù),必須在操作系統(tǒng)狀態(tài)下,輸入主函數(shù)所在的可執(zhí)行文件名,
35、以及所需的實(shí)參,然后回車即可。命令行的一般格式為: 可執(zhí)行文件名實(shí)參 實(shí)參2本案例程序的用法:lock +|- 主函數(shù)main()的形參案例9.13 用同一程序?qū)崿F(xiàn)文件的加密和解密。約定:程序的可執(zhí)行文件名為lock.exe;其用法為:lock +|- 其中“+”為加密,“-”為解密。/*案例代碼文件名:AL9_13.C*/*程序功能:帶參主函數(shù)的應(yīng)用示例*/main(int argc, char *argv) char c; if (argc != 3) printf(參數(shù)個(gè)數(shù)不對(duì)!n); else c=*argv1;/*截取第二個(gè)實(shí)參字符串的第一個(gè)字符*/ switch(c) case +
36、: /*執(zhí)行加密*/ /*加密程序段*/ printf(執(zhí)行加密程序段。n); break; case -: /*執(zhí)行解密*/ /*解密程序段*/ printf(執(zhí)行解密程序段。n); break; default: printf(第二個(gè)參數(shù)錯(cuò)誤!n); 形參說明(1)形參argc是命令行中參數(shù)的個(gè)數(shù)(可執(zhí)行文件名本身也算一個(gè))。在本案例中,形參argc的值為3 (lock、+|-、文件名)。(2)形參argv是一個(gè)字符指針數(shù)組,即形參argv首先是一個(gè)數(shù)組(元素個(gè)數(shù)為形參argc的值),其元素值都是指向?qū)崊⒆址闹羔槨T诒纠?,元素argv0指向第1個(gè)實(shí)參字符串“l(fā)ock”,元素argv1
37、 指向第2個(gè)實(shí)參字符串“+|-”,元素argv2指向第3個(gè)實(shí)參字符串“被處理的文件名”。函數(shù)以及函數(shù)名函數(shù)的定義functiontype functionname( argument list) function body函數(shù)名稱函數(shù)名稱是一個(gè)常量(不能修改)。利用函數(shù)名稱可以訪問(調(diào)用)函數(shù);那么我們可以定義函數(shù)指針,利用函數(shù)指針來完成對(duì)函數(shù)調(diào)用。指向函數(shù)的指針一個(gè)通常的函數(shù)調(diào)用的例子:void MyFun(int x);/此處的聲明也可寫成:void MyFun( int );void main(int argc, char* argv)MyFun(10);/這里是調(diào)用MyFun(10);
38、函數(shù)void MyFun(int x)/這里定義一個(gè)MyFun函數(shù)printf(“%dn”,x);從功能上或者說從數(shù)學(xué)意義上理解函數(shù),MyFun函數(shù)名代表的是一個(gè)功能(或是說一段代碼)。就象某一數(shù)據(jù)變量的內(nèi)存地址可以存儲(chǔ)在相應(yīng)的指針變量中一樣,函數(shù)的首地址也以存儲(chǔ)在某個(gè)指針變量里。這樣,就可以通過這個(gè)函數(shù)指針變量來調(diào)用所指向的函數(shù)了。在C系列語言中,任何一個(gè)變量,總是要先聲明,之后才能使用的。那么,函數(shù)指針變量也應(yīng)該先聲明。以上面的例子為例,來聲明一個(gè)可以指向MyFun函數(shù)的函數(shù)指針變量FunP:void (*FunP)(int) ;也可寫成void (*FunP)(int x);函數(shù)指針變量
39、的聲明格式如同函數(shù)MyFun的聲明一樣,只不過把MyFun改成(*FunP)而已。這樣就有了一個(gè)能指向MyFun函數(shù)的指針了。當(dāng)然,這個(gè)指針變量也可以指向其它所有具有相同函數(shù)格式的函數(shù)。函數(shù)指針變量的聲明有了FunP指針變量后,我們就可以對(duì)它賦值,指向MyFun,然后通過FunP來調(diào)用MyFun函數(shù)了。void MyFun(int x);void (*FunP)(int );/也可聲明成void(*FunP)(int x),但習(xí)慣上一般不這樣。void main(int argc, char* argv)MyFun(10);/這是直接調(diào)用MyFun函數(shù)FunP=MyFun;/對(duì)函數(shù)指針賦值(*
40、FunP)(20);/通過函數(shù)指針FunP來調(diào)用MyFun函數(shù) / FunP(20);/也可以寫成這樣通過函數(shù)指針變量調(diào)用函數(shù)void MyFun(int x)/定義了MyFun函數(shù)printf(“%dn”,x);MyFun與FunP的類型是相吻合的:type *與type *的關(guān)系。函數(shù)MyFun好像是一個(gè)int的數(shù)組名(常量),而FunP則是一個(gè)int *的指針變量。int A10,*pi;pi = A;/與FunP=MyFun比較。你的感覺呢?繼續(xù)看以下幾種情況:(這些可都是可以正確運(yùn)行的代碼?。﹙oid main(int argc, char* argv)MyFun(10);/調(diào)用My
41、Fun(10);函數(shù)FunP=MyFun;/將函數(shù)地址賦給指針變量(*MyFun)(10);/函數(shù)名MyFun也可以有這樣的調(diào)用格式,就如同用(*FunP)(20);來調(diào)用MyFun函數(shù)真的是可以這樣的!依據(jù)以往的知識(shí)和經(jīng)驗(yàn)來推理本篇的“新發(fā)現(xiàn)”,必定會(huì)推斷出以下的結(jié)論:1. MyFun的函數(shù)名與FunP函數(shù)指針都是一樣的,即都是函數(shù)指針。MyFun函數(shù)名是一個(gè)函數(shù)指針常量,而FunP是一個(gè)函數(shù)數(shù)指針變量,這是它們的關(guān)系。2. 但函數(shù)名調(diào)用如果都得如(*MyFun)(10);這樣,那書寫與讀起來都是不方便和不習(xí)慣的。所以C語言的設(shè)計(jì)者們才會(huì)設(shè)計(jì)成可允許MyFun(10);這種形式地調(diào)用(這樣方
42、便多了并與數(shù)學(xué)中的函數(shù)形式一樣)。3. 為統(tǒng)一起見,F(xiàn)unP函數(shù)指針變量也可以FunP(10)的形式來調(diào)用。4. 賦值時(shí),即可FunP=&MyFun形式,也可FunP=MyFun。 定義函數(shù)的指針類型:就像自定義數(shù)據(jù)類型一樣,我們也可以先定義一個(gè)函數(shù)指針類型,然后再用這個(gè)類型來聲明函數(shù)指針變量。先給你一個(gè)自定義數(shù)據(jù)類型的例子。typedef int* PINT;/為int* 類型定義了一個(gè)PINT的 別名void main()int x;PINT px=&x;/與int * px=&x;是等價(jià)的。PINT類型 其實(shí)就是int * 類型.*px=10; /px就是int*類型的變量.下面我們來看
43、一下函數(shù)指針類型的定義及使用:(請(qǐng)與上對(duì)照!)void MyFun(int x);/此處的聲明也可寫成: void MyFun( int );typedef void (*FunType)(int );/這樣只是定義一個(gè)函數(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 ); 前加了一個(gè)typedef 。這只是定義一個(gè)名為FunType的函數(shù)指針類型,而不是FunType變量。然后使用FunType FunP;這句就如PINT px;一樣地聲明了一個(gè)FunP變量。其它相同。整個(gè)程序完成了相同的事。這樣做法的好處是:有了FunType類型后,我們就可以同樣地、很方便地用FunType類型來聲明多個(gè)同類型的函數(shù)指針變量了。如下:FunType FunP2,F(xiàn)unP3; 定義函數(shù)指針類型解析函數(shù)指針定義 typedef datatype ( *FuncPointerName) (argumentlist); FuncPointerName就是一個(gè)函數(shù)指針類型F
45、uncPointerName pfunc1, pfunc1; pfunc1和pfunc2就是兩個(gè)函數(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ù)的指針進(jìn)行函數(shù)調(diào)用pfun1 = fun2; /指向函數(shù)的指針可以被修改int a = (*pfun1)(bcdefg, 6);各種指針 請(qǐng)說明各指針的含義: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ù)組的每個(gè)元素都是指向二維數(shù)組的指針復(fù)雜指針解析的方法分解法首先從未定義的標(biāo)識(shí)符起往右看,當(dāng)遇到圓括號(hào)時(shí),就掉轉(zhuǎn)方向往左看。解析完圓括號(hào)里面所有的東西,就整個(gè)給它一個(gè)命名。將命名與
47、外面的東西再重復(fù)這個(gè)過程直到將整個(gè)聲明解析完畢。外面的東西其實(shí)是類型。 第一個(gè)例子 int (*func)(int *p);首先找到未定義的標(biāo)識(shí)符,就是func,它的外面有一對(duì)圓括號(hào),而且左邊是個(gè)*號(hào),這說明func是個(gè)指針,然后跳出這個(gè)圓括號(hào),并給它一個(gè)命名,比如F 。再看右邊,也是一個(gè)圓括號(hào),這說明F是個(gè)函數(shù),而func是個(gè)指向這類函數(shù)的指針,這類函數(shù)具有int*類型的形參,返回值類型是int。 第二個(gè)例子 int (*func)(int *p, int (*f)(int*);func被一對(duì)括號(hào)包含,且左邊有個(gè)*號(hào),說明func是個(gè)指針,給它一個(gè)命名,比如F。其右邊也有個(gè)括號(hào),那么func
48、是個(gè)指向函數(shù)的指針,這類函數(shù)具有int *和int (*)(int*)形參,返回值為int類型。再來看形參int (*f)(int*),類似前面的解釋,f也是一個(gè)函數(shù)指針,指向的函數(shù)具有int*類型的形參,返回值為int 第三個(gè)例子 int (*func5)(int *p);func右邊是一個(gè)運(yùn)算符,說明func是具有5個(gè)元素的數(shù)組,func的左邊有個(gè)*,說明func的數(shù)組元素是指針,要注意這里的*不是修飾func的,而是修飾func5的,原因是運(yùn)算符優(yōu)先級(jí)比*高,func先跟5結(jié)合。給它一個(gè)命名,比如F ??从疫?,也是一對(duì)圓括號(hào),說明func數(shù)組的元素是函數(shù)類型的指針,它所指向的函數(shù)具有in
49、t*類型的形參,返回值類型為int。所以func是“每個(gè)元素都是指向函數(shù)的指針的數(shù)組名”。 第四個(gè)例子 int (* (*func)5 )(int *p); func被圓括號(hào)包含,左邊又有*,那么func是個(gè)指針,右邊是個(gè)號(hào),那么說明func是一個(gè)指向數(shù)組的指針,給它一個(gè)命名,比如F ,那么其余部分就是數(shù)組的類型了。往左看,有個(gè)*號(hào),說明這個(gè)數(shù)組的元素是指針,跳出括號(hào),右邊又有一對(duì)括號(hào),說明是個(gè)形參。這說明數(shù)組的元素是指向函數(shù)的指針??偨Y(jié)一下,就是:func是一個(gè)指向二維數(shù)組的指針,這個(gè)數(shù)組的每個(gè)元素都是函數(shù)指針,這些指針指向具有int*形參,返回值為int類型的函數(shù)。 從以上解析可以看出,類
50、型是揭示謎底的法寶。 第五個(gè)例子 int (* (*func)(int *p) )5;func是一個(gè)函數(shù)指針,這類函數(shù)具有int*類型的形參(紅的部分)。返回值(黑的部分)是指向數(shù)組的指針,該指針?biāo)赶虻臄?shù)組的元素是具有5個(gè)int元素的數(shù)組 int (* )5 。 第六個(gè)例子 char (*(* fun () )4) (int *p) 從fun ()看fun是個(gè)函數(shù),將其命名為F,得: char (*(* F )4) (int *p) ;分析(* F )4,F(xiàn)是指向二維數(shù)組的指針,命名為P得char (*P) (int *p) ;可見P是個(gè)指向函數(shù)的指針。于是,fun是一個(gè)函數(shù),返回值是指向二
51、維數(shù)組的指針。所指向的數(shù)組的元素都是指針,每個(gè)指針都可以指向函數(shù)。這類函數(shù)具有返回char類型、形參是一個(gè)且類型是整型指針的架構(gòu)。 第七個(gè)例子 int *(*(*func)(int *(*)10)10;func是一個(gè)指向函數(shù)指針,這類函數(shù)用指向二維的指針數(shù)組的指針做參數(shù),返回值的也是同類型指針。此句整體定義了一個(gè)指向函數(shù)的指針數(shù)組。若定義了typedef int * (*pa)10; 后,就可以用 pa(*func)(pa); 來代替該句。 練習(xí)解讀: char ( * ( * ( * q ) ( ) ) ) ( ); ( * q ) ( ) k 然后其它的東西都是它的類型 ( * k ) m
52、 這個(gè)數(shù)組的類型在下面 char ( * m ) ( ) 這是數(shù)組的類型總結(jié):q是個(gè)指向函數(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個(gè)int元素的二維數(shù)組,而指向三維數(shù)組的指針又是另一個(gè)三維指針數(shù)組的元素。2) func是函數(shù)指針,這類函數(shù)的返回值是指向數(shù)組的指針,所指向數(shù)組的元素也是函數(shù)指針,
53、指向的函數(shù)具有int*形參,返回值為int。3) func是個(gè)三維指針數(shù)組,數(shù)組元素是函數(shù)指針,這類函數(shù)具有int*的形參,返回值是指向數(shù)組的指針,所指向的數(shù)組的元素是具有5個(gè)int元素的數(shù)組。指向指針的指針int i = 20;int *pi = &i;int *ppi = 指針變量ppi的內(nèi)容就是指針變量pi的起始地址。于是ppi的值是多少呢?10000。是pi的址.*ppi的值是多少呢2006,即pi的值,也是i的址。*ppi的值是多少呢?20,即i的值,也是*pi的值。它可以指向指針,亦可以指向指針數(shù)組。設(shè)計(jì)一個(gè)函數(shù):void find1(char array, char search
54、, char * pa)要求:這個(gè)函數(shù)參數(shù)中的數(shù)組array是以0值為結(jié)束的字符串,要求在字符串a(chǎn)rray中查找與參數(shù)search給出的字符相同的字符。如果找到,通過第三個(gè)參數(shù)(pa)返回array字符串中首先碰到的字符的地址。如果沒找到,則為pa為0。依題意,實(shí)現(xiàn)代碼如下。指向指針的指針應(yīng)用實(shí)例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、覺得這個(gè)函數(shù)能實(shí)現(xiàn)所要求的功能嗎?下面調(diào)用這個(gè)函數(shù)試試。void main()char str=“afsdfsdfdf”;/待查找的字符串char a=d;/設(shè)置要查找的字符char * p=0;find1(str,a,p);/調(diào)用函數(shù)以實(shí)現(xiàn)所要操作。if (0=p )printf (“沒找到!n”);/如果沒找到則輸出此句elseprintf(“找到了,p=%d”,p);/如果找到則輸出此句上面代碼,你認(rèn)為會(huì)是輸出什么呢?明明a值為d,而str字符串的第四個(gè)字符就是d,應(yīng)該找得到呀!分析:先看函數(shù)定義處:void find1(char array, char search, char * pa)再看調(diào)用處:find1(str,a,p);請(qǐng)仔細(xì)考慮此時(shí)形實(shí)結(jié)合所發(fā)生的事:array得到了數(shù)組名str,search得到了a的值, pa得到了p的值(而非p的地址)!可見盡管使用了指針,也并沒實(shí)現(xiàn)傳址,當(dāng)實(shí)參形參都是指針時(shí),它們也僅僅是傳值。畫出內(nèi)存使用圖示就清楚了。修正:void find2(char array, char search, char * ppa)int i;for
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 供貨協(xié)議合同范例酒水
- 廠區(qū)監(jiān)控維保合同范例
- 確保資金使用效率的管理措施計(jì)劃
- 公共場所安保人員培訓(xùn)計(jì)劃
- 幼兒園多元智能發(fā)展計(jì)劃
- 心理契約與員工忠誠度計(jì)劃
- 新媒體對(duì)傳統(tǒng)閱讀習(xí)慣的影響計(jì)劃
- 改進(jìn)供水調(diào)度系統(tǒng)計(jì)劃
- 《清鎮(zhèn)市站街鎮(zhèn)龍灘前明鋁鐵礦山有限公司清鎮(zhèn)市站街鎮(zhèn)龍灘前明鋁鐵礦(延續(xù))礦產(chǎn)資源綠色開發(fā)利用方案(三合一)》評(píng)審意見
- 四川省釩鈦產(chǎn)業(yè)投資發(fā)展有限公司四川省鹽邊縣紅格南釩鈦磁鐵礦二合一方案情況
- 計(jì)算機(jī)系統(tǒng)原理13015習(xí)題答案
- 臨床實(shí)驗(yàn)室精液常規(guī)檢驗(yàn)中國專家共識(shí)
- 人工智能倫理與社會(huì)影響的討論
- 【音樂】繽紛舞曲-青年友誼圓舞曲課件 2023-2024學(xué)年人音版初中音樂七年級(jí)上冊(cè)
- DB-T29-260-2019天津市建筑物移動(dòng)通信基礎(chǔ)設(shè)施建設(shè)標(biāo)準(zhǔn)
- 吉利汽車經(jīng)銷商運(yùn)營手冊(cè)
- 《如何處理人際關(guān)系》課件
- 社區(qū)消防網(wǎng)格員培訓(xùn)課件
- 太陽能路燈施工方案
- 前列腺炎的護(hù)理課件
- 外墻防水膠驗(yàn)報(bào)告模板
評(píng)論
0/150
提交評(píng)論