程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++基礎之指針的詳細介紹(二)

C++基礎之指針的詳細介紹(二)

編輯:C++入門知識

本篇是上一篇的續篇,接著為大家介紹C++中的指針

在堆上分配內存

前面已經說過,所謂的在堆上分配就是運行時期向操作系統申請內存,而要向操作系統申請內存,不同的操作系統提供了不同的接口,具有不同的申請內存的方式,而這主要通過需調用的函數原型不同來表現。由於C++是一門語言,不應該是操作系統相關的,所以C++提供了一個統一的申請內存的接口,即new操作符。如下:

  1. unsigned long *pA = new unsigned long;   
  2. *pA = 10;  
  3. unsigned long *pB = new unsigned long[ *pA ]; 

上面就申請了兩塊內存,pA所指的內存即pA的值所對應的內存)是4字節大小,而pB所指的內存是4*10=40字節大小。應該注意,由於new是一個操作符,其結構為new <類型名>[<整型數字>].它返回指針類型的數字,其中的<類型名>指明了什麼樣的指針類型,而後面方括號的作用和定義數組時一樣,用於指明元素的個數,但其返回的並不是數組類型,而是指針類型。

應該注意上面的new操作符是向操作系統申請內存,並不是分配內存,即其是有可能失敗的。當內存不足或其他原因時,new有可能返回數值為0的指針類型的數字以表示內存分配失敗。即可如下檢測內存是否分配成功。

  1. unsigned long *pA = new unsigned long[10000];  
  2. if !pA )? // 內存失敗!做相應的工作 

上面的if是判斷語句,下篇將介紹。如果pA為0,則!pA的邏輯取反就是非零,故為邏輯真,進而執行相應的工作。

只要分配了內存就需要釋放內存,這雖然不是必須的,但是作為程序員,它是一個良好習慣資源是有限的)。為了釋放內存,使用delete操作符,如下:

  1. delete pA;   
  2. delete[] pB; 

注意delete操作符並不返回任何數字,但是其仍被稱作操作符,看起來它應該被叫做語句更加合適,但為了滿足其依舊是操作符的特性,C++提供了一種很特殊的數字類型——void.其表示無,即什麼都不是,因此delete其實是要返回數字的,只不過返回的數字類型為void罷了。

注意上面對pA和pB的釋放不同,因為pA按照最開始的書寫,是new unsigned long返回的,而pB是new unsigned long[ *pA ]返回的。所以需要在釋放pB時在delete的後面加上“[]”以表示釋放的是數組,不過在VC中,不管前者還是後者,都能正確釋放內存,無需“[]”的介入以幫助編譯器來正確釋放內存,因為以Windows為平台而開發程序的VC是按照Windows操作系統的方式來進行內存分配的,而Windows操作系統在釋放內存時,無需知道欲釋放的內存塊的長度,因為其已經在內部記錄下來這種說法並不准確,實際應是C運行時期庫干了這些事,但其又是依賴於操作系統來干的,即其實是有兩層對內存管理的包裝,在此不表)。

類型修飾符type-specifier)

類型修飾符,即對類型起修飾作用的符號,在定義變量時用於進一步指明如何操作變量對應的內存。因為一些通用操作方式,即這種操作方式對每種類型都適用,故將它們單獨分離出來以方便代碼的編寫,就好像水果。吃蘋果的果肉、吃梨的果肉,不吃蘋果的皮、不吃梨的皮。這裡蘋果和梨都是水果的種類,相當於類型,而“XXX的果肉”、“XXX的皮”就是用於修飾蘋果或梨這種類型用的,以生成一種新的類型——蘋果的果肉、梨的皮,其就相當於類型修飾符。

本文所介紹的數組和指針都是類型修飾符,之前提過的引用變量的“&”也是類型修飾符。

類型修飾符只在定義變量時起作用,如前面的

  1. unsigned long a, b[10], *pA = &a, &rA = a; 

這裡就使用了上面的三個類型修飾符——“[]”、“*”和“&”。上面的unsigned long暫且叫作原類型,表示未被類型修飾符修飾以前的類型。下面分別說明這三個類型修飾符的作用。

數組修飾符“[]”——其總是接在變量名的後面,方括號中間放一整型數c以指明數組元素的個數,以表示當前類型為原類型c個元素連續存放,長度為原類型的長度乘以c.因此long a[10];就表示a的類型是10個long類型元素連續存放,長度為10*4=40字節。而long a[10][4];就表示a是4個long[10]類型的元素連續存放,其長度為4*40=160字節。

相信已經發現,由於可以接多個“[]”,因此就有了計算順序的關系,為什麼不是10個long[4]類型的元素連續存放而是倒過來?類型修飾符的修飾順序是從左向右進行計算的。故short *a[10];表示的是10個類型為short*的元素連續存放,長度為10*4=40字節。

指針修飾符“*”——其總是接在變量名的前面,表示當前類型為原類型的指針。故:

  1. short a = 10, *pA = &a, **ppA = &pA; 

注意這裡的ppA被稱作多級指針,即其類型為short的指針的指針,也就是short**.而short **ppA = &pA;的意思就是計算pA的地址的值,得一類型為short*的地址類型的數字,然後“&”操作符將此數字轉成short*的指針類型的數字,最後賦值給變量ppA.
如果上面很昏,不用去細想,只要注意類型匹配就可以了,下面簡要說明一下:假設a的地址為2000,則pA的地址為2002,ppA的地址為2006.

對於

  1. pA = &a; 

先計算“&a”的值,因為a等同於地址,則“&”發揮作用,直接將a的地址這個數字轉成long*類型並返回,然後賦值給pA,則pA的值為2000.

對於

  1. ppA = &pA; 

先計算“&pA”的值,因為pA等同於地址,則“&”發揮作用,直接將pA的地址這個數字轉成long**類型因為pA已經是long*的類型了)並返回,然後賦值給ppA,則ppA的值為2002.

引用修飾符“&”——其總是接在變量名的前面,表示此變量不用分配內存以和其綁定,而在說明類型時,則不能有它,下面說明。由於表示相應變量不用分配內存以生成映射,故其不像上述兩種類型修飾符,可以多次重復書寫,因為沒有意義。且其一定在“*”修飾符的右邊,即可以

  1. long **&a = ppA; 

但不能

  1. long *&*a;  
  2. 或  
  3. long &**a; 

因為按照從左到右的修飾符計算順序,long*&*表示long的指針的引用的指針,引用只是告知編譯器不要為變量在棧上分配內存,實際與類型無關,故引用的指針是無意義的。

而long&**則表示long的引用的指針的指針,同上,依舊無意義。同樣

  1. long &a[40];//錯誤的 

因為其表示分配一塊可連續存放類型為long的引用的40個元素的內存,引用只是告知編譯器一些類型無關信息的一種手段,無法作為類型的一種而被實例化。

應該注意引用並不是類型但出於方便,經常都將long的引用稱作一種類型),而

  1. long **&rppA = &pA;是錯誤的 

因為上句表示的是不要給變量rppA分配內存,直接使用“=”後面的地址作為其對應的地址,而&pA返回的並不是地址類型的數字,而是指針類型,故編譯器將報類型不匹配的錯誤。

但是即使

  1. long **&rppA = pA; 

也同樣失敗,因為long*和long**是不同的,不過由於類型的匹配,下面是可以的:

  1. long a = 10, *pA = &a, **ppA = &pA, *&rpA1 = *ppA, *&rpA2 = * ppA + 1 ); 

類型修飾符和原類型組合在一起以形成新的類型,如long*&、short *[34]等,都是新的類型,應注意前面new操作符中的<類型名>要求寫入類型名稱,則也可以寫上前面的long*等,即:

  1. long **ppA = new long*[45]; 

即動態分配一塊4*45=180字節的連續內存空間,並將首地址返回給ppA.同樣也就可以:

  1. long ***pppA = new long**[2];  
  2. 而  
  3. long **pA)[10] = new long*[20][10]; 

也許看起來很奇怪,其中的pA的類型為long **)[10],表示是一個有10個long*元素的數組的指針,而分配的內存的長度為4*10)*20=800字節。因為數組修飾符“[]”只能放在變量名後面,而類型修飾符又總是從左朝右計算,則想說明是一個10個long元素的數組的指針就不行,因為放在左側的“*”總是較右側的“[]”先進行類型修飾。

故C++提出上面的語法,即將變量名用括號括起來,表示裡面的類型最後修飾,

故:

  1. long *a)[10];  
  2. 等同於  
  3. long *a[10];  

  1. long *&aa)[10] = a; 

也才能夠正確,否則按照前面的規則,使用

  1. long *&aa[10] = a; 

將報錯前面已說明原因)。而

  1. long **pA)[10] = &a; 

也就能很正常地表示我們需要的類型了。因此還可以

  1. long **&rpA)[10] = pA;  
  2. 以及  
  3. long ***ppA)[10] = &pA; 

希望通過本文的介紹,能夠讓你清楚的明白C++中的指針概念以及用法。

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