C語言:指針學習以及理解

C語言:關於指針學習以及理解

一、什麼是指針

首先,指針是一種數據類型,使用它定義的變量叫指針變量。指針變量是一個特殊的變量,它裏面存儲的數值被解釋成爲內存裏的一個地址指針的值是一個地址

指針的四個要素?:指針的類型、指針所指向的類型、指針的值 (指針所指向的內存區,即指向的地址) 、指針本身所佔據的內存區(指針本身的地址)。
在這裏插入圖片描述

(根據上圖)舉個例子:
int a=111;
int *p = &a;

上面兩句語句中可得出:
p 是指針變量
指針類型:  p 是一個 int 類型的指針
指向類型:  p 指向一個 int 類型的數據 a
指針的值:  p 的值是0x30009 ( 即 p 指向的地址是0x30009 )  
指針的地址:p 所佔據的內存區 (即 p 本身的地址爲0x30001)    

所以要注意:指針的值指針的地址 不是同一個東西,一個代表 p ,另一個代表&p 。

二、爲什麼使用指針、什麼情況下使用指針

1、函數之間無法通過傳參共享變量。
函數的形參變量屬於被調用於者,實參屬於調用者,函數之間的變量名字空間相互獨立是可以重名的,函數之間的數據傳遞都是值傳遞(賦值、內存拷貝)。
2、使用指針可以優化函數之間傳參的效率。(無需傳遞數組中所有參數,直接傳遞數組首地址)
3、堆內存無法與標識符建立聯繫,只能配合指針使用。

三、如何使用指針

? 定義:類型 * 變量名p;
1、指針變量與普通變量使用方法有很大區別,一般以 p 結尾,與普通變量區分開。
2、* 表示此變量是指針變量,一個 * 只能定義出一個指針變量,不能連續定義。

   int* p1,p2,p3;  // p1是指針,p2,p3是int類型變量
   int *p1,*p2,*p3; // 三個指針變量

3、類型表示的是存儲是什麼類型變量的地址,它決定當通過地址訪問這塊內存時訪問的字節數。
4、指針變量的默認值也是不確定,一般初始化爲NULL(空指針)。

? 賦值操作:指針變量 = 地址

    棧地址賦值:
        int num = 0;
        int* p = NULL;
        p = #
    堆地址賦值:
        int* p = NULL;
        p = malloc(4);

? 解引用(根據地址訪問內存): * 指針變量名 <=> 變量
1、根據變量中存儲的內存編號(即地址)去訪問內存中的數據,訪問多少個字節要根據指針變量的類型。
2、如果指針變量中存儲的地址出錯,此時可能發生段錯誤(這是賦值產生的錯誤)。

四、使用指針要注意的問題

? 空指針:

1、指針變量的值爲NULL(大多數是0,也有特殊情況是1),這種指針變量叫空指針,空指針不能進行解引用(* 指針變量),NULL被操作系統當作是復位地址(存儲了系統重啓所需要的數據),當操作系統察覺到程序訪問NULL位置的數以的時就會向程序發送段錯誤的信號,程序就會死亡。

2、空指針還被當作錯誤標誌,如果一個函數的返回值是指針類型,實際返回的值是NULL,則說明函數執行失敗或出錯。

在C語言代碼中應該杜絕對空指針進行解引用,當使用來歷不明的指針(調用者提供的)前應該先判斷是否爲NULL。

void func(int* p)
{
     if(NULL == p)
     {
      	//函數內容
     }
}

? 野指針:指針變量的值是不確定的或都是無效的,這種指針叫野指針。

使用野指針不一定會出問題,可能產生的後果如下
1、一切正常
2、段錯誤
3、髒數據

  • 雖然野指針不一定會出錯,但野指針比空指針的危險更大,因爲野指針是無法判斷出來、也無法測試出來,也就意味着一旦產生無法杜絕。

  • 雖然野指針無法判斷也無法測試出來,但是所有的野指針都是人爲製造出來的,最好方法就是不生產野指針
    1、定義指針變量時要初始化。
    2、不返回局部變量的地址。
    3、資源釋放後,指向它的指針及時置空。

五、指針與數組的關係

數組名就是個指針(常指針),數組名與數組首地址是映射關係,而指針是指向關係。
由於數組名就是指針,所以數組名可以使用指針的解引用運算符,而指針也可以使用數組的 [ ] 運算符。
注意:使用數組當函數的參數時,數組會蛻變成指針,長度也就丟失,因此需要額外添加一個參數用來傳遞數組的長度。

六、指針的運算

指針的本質就是個整數,因此從語法上來說整數能使用的運算符它都能使用。

  • 不是所有的運算符對指針運算都是有意義的。
    指針+ 整數 <=> 指針+ 寬度* 整數 向右移動
    指針 - 整數 <=> 指針 - 寬度* 整數 向左移動
    指針 - 指針 <=> 指針 - 指針/寬度 計算出兩個指針之間相隔多少個元素。

七、指針與const配合

 const int* p; 		保護指針指向的數據,不能通過指針解引用修改指針指向的內存地址中的值。
 int const *p; 		//同上
 int * const p; 		保護指針變量,指針變量(地址)初始化之後不能再顯式的賦值,即指針的值不能被顯示的修改。
 const int *const p; 既不能修改指針的值,也不能修改指針指向的內存地址中的值。
 int const * const p;//同上。

八、什麼是二級指針,什麼情況下使用

二級指針:指向指針的指針。

一級指針與二級指針的比較:一級指針的值爲地址,該值(即地址)需要空間來存放,存放在指針的地址中,是空間就具有地址,二級指針就是爲了獲取這一空間的地址。一級指針所關聯的是其值(一個地址)名下空間裏的數據,這個數據可以是任意類型並做任意用途,但二級指針所關聯的數據只有一個類型一個用途,就是地址。在這裏插入圖片描述

一級指針(圖中 p): 其指針的值是一個變量的地址(a的地址),對其解引用得到的是一級指針所指向變量的值(a的值10086)。
二級指針 (圖中 pp):其指針的值是一個指針的地址(p的地址),對其解引用得到的是二級指針所指向的指針的值(p的值0x100A2)

指針的用途:提供目標的讀取或改寫,而二級指針就是對於內存地址的讀取和改寫。

二級指針作爲函數參數的作用:在函數外部定義一個指針p,在函數內給指針賦值,函數結束後對指針p生效,那麼我們就需要二級指針。

九、指針函數和函數指針

函數具有可賦值給指針的物理內存地址,一個函數的函數名就是一個指針,它指向函數的代碼。一個函數的地址是該函數的進入點,也是調用函數的地址。函數的調用可以通過函數名,也可以通過指向函數的指針來調用。函數指針還允許將函數作爲變元傳遞給其他函數。

指針函數:簡單的來說,就是一個返回指針的函數,其本質是一個函數,而該函數的返回值是一個指針。

聲明格式爲:類型標識符 *函數名(參數表)

  int fun(int x,int y);
  這種函數應該都很熟悉,其實就是一個函數,然後返回值是一個 int 類型,是一個數值。
    

  int * fun(int x,int y);
  這和上面那個函數唯一的區別就是在函數名前面多了一個*號,而這個函數就是一個指針函數。  
  其返回值是一個 int 類型的指針,是一個地址。

這樣描述應該很容易理解了,所謂的指針函數也沒什麼特別的,和普通函數對比不過就是其返回了一個指針(即地址值)而已。

函數指針:其本質是一個指針變量,該指針指向這個函數。總結來說,函數指針就是指向函數的指針。
聲明格式:類型說明符 (*函數名) (參數)

  int (*fun)(int x,int y);
函數指針是需要把一個函數的地址賦值給它,有兩種寫法:

  fun = &Function;
  fun = Function;

取地址運算符&不是必需的,因爲一個函數標識符就表示了它的地址,

如果是函數調用,還必須包含一個圓括號括起來的參數表。

調用函數指針的方式也有兩種:

x = (*fun)();
x = fun();

兩種方式均可,其中第二種看上去和普通的函數調用沒啥區別,如果可以的話,建議使用第一種,因爲可以清楚的指明這是通過指針的方式來調用函數。當然,也要看個人習慣,如果理解其定義,隨便怎麼用都行啦。

十、數組指針和指針數組

數組指針(也稱行指針)
定義 int (*p)[n];
()優先級高,首先說明p是一個指針,指向一個整型的一維數組,這個一維數組的長度是n,也可以說是p的步長。也就是說執行p+1時,p要跨過n個整型數據的長度。
如要將二維數組賦給一指針,應這樣賦值:

  int a[3][4];
  int (*p)[4]; //該語句是定義一個數組指針,指向含4個元素的一維數組。
  p=a;        //將該二維數組的首地址賦給p,也就是a[0]或&a[0][0]
  p++;       //該語句執行過後,也就是p=p+1;p跨過行a[0][]指向了行a[1][]

  所以數組指針也稱指向一維數組的指針,亦稱行指針。

指針數組
定義 int * p[n];
[]優先級高,先與p結合成爲一個數組,再有int* 說明這是一個整型指針數組,它有n個指針類型的數組元素。這裏執行p+1時,則p指向下一個數組元素,這樣賦值是錯誤的:p=a;因爲p是個不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它們分別是指針變量可以用來存放變量地址。但可以這樣 * p=a; 這裏*p表示指針數組第一個元素的值,a的首地址的值。
如要將二維數組賦給一指針數組:

  int *p[3];
  int a[3][4];
  p++; //該語句表示p數組指向下一個數組元素。注:此數組每一個元素都是一個指針
  for(i=0;i<3;i++)
  p[i]=a[i]
  這裏int *p[3] 表示一個一維數組內存放着三個指針變量,分別是p[0]、p[1]、p[2]所以要分別賦值。

這樣兩者的區別就豁然開朗了,數組指針只是一個指針變量,似乎是C語言裏專門用來指向二維數組的,它佔有內存中一個指針的存儲空間。指針數組是多個指針變量,以數組形式存在內存當中,佔有多個指針的存儲空間

優先級:()>[]>*

(近期更新中…)

十一、結構體指針

十二、結構體成員指針

十三、指針與堆內存配合

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章