程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 說說對C語言指針的理解,c語言指針

說說對C語言指針的理解,c語言指針

編輯:關於C語言

說說對C語言指針的理解,c語言指針


我能寫出來的暫時都還是很淺層次的內容,水平有限請諒解,期盼大神的指正。

 

指針困擾了無數學習編程的人,或許你的老師會告訴你,指針比較難學。

然而,不要被嚇到,本寶寶就是當時被老師的話唬住所以學習指針那章的時候都沒心情聽課。(說得像講別的內容時我聽了似的,just kidding)

導致了學習鏈表的時候各種臥槽。

 

*************************** 基本介紹 **************************

 

程序中數據的使用往往是以變量的形式出現,每個變量都對應若干存儲單元,變量的值存儲在存儲單元中,通過對變量的引用和賦值就可以使用或修改存儲在存儲單元中的數據。

                                                                                                                                                            ——《C語言程序設計與實踐》

如果一個變量的地址存放在另一個變量中,則存放地址的變量叫指針變量。

指針就是地址,指針運算就是根據地址得到那個地址下存放的數據。

常用的是一級指針,二級指針一般比較少用。

 

*************************較為形象的理解 ************************

 

比如:讓你去教學樓A214教室找一個人a,A214就是這個人所在的地址,A214就是一個一級指針,你有這個地址,你就能找到這個人。

   類似的,二級指針則是:A214教室裡沒有這個人,但是門上貼著另一個地址:A215,然後你來到A215,找到了這個人,那麼A214就是一個二級指針

     因為你要經過兩次尋找,才能找到這個人。

 

**************************** 進入正題 *************************

指針的聲明及初始化:

 1  //第一種:聲明並初始化
 2  
 3  int a = 3;
 4  int *p = a;  
 5 /* 定義指針變量 p 為指向整型數據 a 的指針,這一句寫成 int *p = &a 同樣是對的,一個意思,這裡的 *號只是表示P是一個指針,並不是做指針運算。
但是在DEVC++環境下像第四行代碼那樣寫(不寫&符號)會有警告。*/ 6 7 //第二種:僅聲明 8 9 float *p; //定義一個指向單精度型數據的指針變量 p 10 11 // 書面一些講, 定義一個指針變量的一般形式是: 12 // <類型> *<變量標識符> 13 // 變量標識符,就像上面的 p 14 // 指針的英文是pointer

 

 

一段簡單的代碼

 1 #include <stdio.h>
 2 int main()
 3 {
 4     int x,y;
 5     int *px,*py,*p;
 6     px = &x;  // &是取地址符號
 7     py = &y;
 8     x = 21;
 9     y = *px;
10     //指針運算,即取出 px 這個地址(也就是 x 的地址)對應的值賦給 y
11 
12     p = &y;
13     printf("%d,%d,%d,%d,%d\n",*px,*py,x,y,*p);
14     return 0;
15 }
16 
17     /*  輸出結果:分別是對 px 做指針運算得到的21,對 py 做指針運算得到的
18     21,x的值21,y的值21,對p做指針運算得到y的值21  */

 

注意事項:

1. 指針不能指向一個沒有被定義的變量,這也是第一行代碼存在的原因。

2. 指針不能指向具體的數值,例如:int *p = 3,這是錯誤的。

3. 分清楚當前指針是在做指針運算還是表明該指針指向某個變量。

 

第三條個人認為非常重要,不然會理解不了很多東西,不要一看到 * 號就覺得是在做指針運算(即把那個地址對應的值給取出來),因為有的時候只是表明那個變量的指針身份,表指向而已。

 

***************************** 指針與數組 ******************************

 

數組的名字就是它的首地址,這個首地址是不能被改變的,數組可以轉換成指針。

1 int a[3];
2 for(int i = 0; i<3; i++)
3 {
4     *(a+i) = i;
5     //  *(a) = i; a++;  這樣寫是錯誤的,a是指針常量,常量不能被修改
6 }

 

***************************** 指針常量 *******************************

 

形如:int * const pa = a; 

仔細觀察一下,*號在const前面,說明pa是常量。

1 int a = 1;
2 int b = 3;
3 int *const pa = &a;
4 a = b;   //  這句話也可以用*pa = b來代替
5 //  如果寫 pa = &b 顯然是不行的,因為pa這個指針是常量指針
6 printf("%d",*pa);
7 //  輸出結果是3

說白了,不能寫pa = xxxx,但是可以寫*pa = xxxx

 

***************************** 常量指針 ****************************

 

形如:int const *pa = a;

這裡int和const誰前誰後都是一樣的

1 int a = 2;
2 int b = 3;
3 const int *pi = &a; //仔細觀察可以看出 pi 是指向常量的指針
4 pi=&b;     //注意這裡,pi可以在任意時候重新賦值一個新內存地址
5 b = 30;    //如果這裡用*pi=30,是錯誤的
6 printf( “%d”, *pi ) ; 
7  // 輸出是30

有人會疑惑,這明明是指向常量的指針,為什麼指向的那個數據卻從2變成了30?

好吧,是因為pi存的地址是可以變的,其實只是說不能寫*pi = xxxx 而已,不能這樣改變,但是可以用別的方式改變。

對比上面兩端代碼看看,應該就明白了。

 

************************ 為什麼指針變量要分類型 **********************

 

有的人疑惑,如果初始化指針指向一個已經聲明過的變量,是不是可以不用寫指針類型?因為指針類型就是它指向的數據的類型啊?

 

1 int a = 3;
2 char ch = 1; //如果這裡寫'1'後面會輸出49,因為是ASCII碼,ASCII碼中的字符1對應十進制49
3 
4 char *p = &ch; 
5 printf("%d",*p); 

請記住:指針的類型決定這個指針會讀多少個字節的數據出來。(至於怎麼讀,從低往高讀還是從高往低讀,數據存儲是從高到低還是從低到高,我還沒有理解清楚)

想了解的朋友可以到這裡看理解較深的大大寫的數據變量在內存中的存儲方式:http://blog.sina.com.cn/s/blog_abc091cc0101h0a3.html

                                                           堆區,棧區,靜態存儲區詳解:http://my.oschina.net/lxrm/blog/513794

所以聲明了類型會更安全。

p讀取一個字節的數據,也就是ch的內容 :1

然而如果把第四行改成int *p = &ch,最後輸出的就不一定是1了,這要看內存分配,這個指針會讀取4個字節(32位平台下int型占4個字節)的數據,非常危險。

 

************************** 指針在函數中的運用 *************************

 

 請看下面一段代碼

值傳遞:

 1 #include <stdio.h>
 2 
 3 
 4 void fun(int a1,int b1)
 5 {
 6     int temp = a1;
 7     a1 = b1;
 8     b1 = temp;
 9 }
10 
11 int main()
12 {
13     int a = 3;b = 4;
14     fun(a,b);
15     printf("%d %d",a,b);
16     return 0;
17 }
18 
19 // 輸出結果:3 4
// a1 = 4,b1 = 3,a = 3,b = 4

觀察這個函數,簡潔明了啊,就是把兩個參數交換。但是為什麼後面的輸出並沒有把a,b的值交換?
是這樣的:在給函數傳遞參數的時候,隱式地做了賦值,把調用函數那一句話括號裡面的參數賦給了形參。

fun(a,b);    //a1 = a; b1 = b; 

進入函數以後,操作數是被賦值過後的a1和b1,所以只是把被a,b分別賦值後的a1,b1作交換,並不是a,b。

這是初次學習函數的同學們常常犯的錯誤。

 

看看下一段代碼
地址傳遞:

 1 #include <stdio.h>
 2 
 3 void fun(int *pa,int *pb)
 4 {
 5     int temp = *pa;
 6     *pa = *pb;
 7     *pb = temp;
 8 }
 9 
10 int main()
11 {
12     int a=3,b = 4;
13     fun(&a,&b);
14     printf("%d %d",a,b);
15     return 0;
16 }
17 
18 輸出結果:4 3

傳給函數作為參數的是a,b的地址,pa = &a, pb = &b,從這兩個地址下取出來的值*pa,*pb當然就是a,b的值了。

然後再交換,所以交換成功。

 

引用傳遞:

void fun(int &a,int &b)
{
    int temp = a;
    a = b;
    b = temp;
}

int main()
{
    int a = 3,b = 4;
    fun(a,b);
    printf("%d %d",a,b);
    return 0;
}
// 運行結果:4 3

// 這段代碼在DEVC++環境下(codeblock也是這樣,別的環境我沒有試過,可能都會這樣),如果選擇建立.c而不是.cpp文件,使用引用傳遞會報錯,因為引用傳遞嚴格來說不屬於C語言的內容


這三段代碼中的第二段可以視作是C語言的模擬引用傳遞

 

 *******************************指向函數的指針******************************

 

某一數據變量的內存地址可以存儲在相應的指針變量中,函數的首地址也可以存儲在某個函數指針變量裡,這樣就可以通過這個函數指針變量來調用所指向的函數。

函數指針變量和別的變量一樣,也是需要聲明後才能用的,相似於函數void fun(int)的聲明,指向這個函數的指針要寫成void (*pfun)(int)

於是:void fun(int);

         void (*pFun)(int);

這兩行就完成了一個函數指針的聲明。

 

 1 #include <stdio.h>
 2 
 3 void fun(int x)  // 如果此處只是聲明,不寫實現,x可以不寫,只用寫參數數據類型
 4 {
 5     printf("%d\n",x);
 6 }
 7 
 8 int main()
 9 {
10     void (*pFun)(int); //括號內也可以寫int x
11     pFun = &fun;   // 令pFun函數指針指向fun函數
12     fun(10);    // 直接調用函數 
13     (*pFun)(20) ;   //  通過指針調用函數
14     return 0;
15 }
16 
17 // 運行結果:10
18             20

 

看到這裡,希望覺得自己不懂指針的人會感覺指針並不是那麼難懂,雖然我寫的都是很淺層次的內容,水平有限請諒解。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved