天天看點

C++讀書筆記:C++指針

指針可以簡化C++程式設計的執行,還有一些任務,比如動态記憶體配置設定,沒有指針是無法執行的。

每一個變量都有一個記憶體位置,每一個記憶體位置都定義了可使用連字号(&)運算符通路的位址,它表示了在記憶體中的一個位址。

#include <iostream>
 
using namespace std;
 
int main ()
{
   int  var1;
   char var2[10];
 
   cout << "var1 變量的位址: ";
   cout << &var1 << endl;
 
   cout << "var2 變量的位址: ";
   cout << &var2 << endl;
 
   return 0;
}
           

什麼是指針?

指針是一個變量,其值為另一個變量的位址,即,記憶體位置的直接位址。就像其他變量或常量一樣,您必須在使用指針存儲其他變量位址之前,對其進行聲明。指針變量聲明的一般形式為:

type *var-name;

在這裡,type 是指針的基類型,它必須是一個有效的 C++ 資料類型,var-name 是指針變量的名稱。用來聲明指針的星号 * 與乘法中使用的星号是相同的。但是,在這個語句中,星号是用來指定一個變量是指針。以下是有效的指針聲明:

int ip; / 一個整型的指針 */

double dp; / 一個 double 型的指針 */

float fp; / 一個浮點型的指針 */

char ch; / 一個字元型的指針 */

所有指針的值的實際資料類型,不管是整型、浮點型、字元型,還是其他的資料類型,都是一樣的,都是一個代表記憶體位址的長的十六進制數。不同資料類型的指針之間唯一的不同是,指針所指向的變量或常量的資料類型不同。

C++ 中使用指針

使用指針時會頻繁進行以下幾個操作:

定義一個指針變量

把變量位址指派給指針

通路指針變量中可用位址的值

這些是通過使用一進制運算符 * 來傳回位于操作數所指定位址的變量的值。下面的執行個體涉及到了這些操作:

#include <iostream>
 
using namespace std;
 
int main ()
{
   int  var = 20;   // 實際變量的聲明
   int  *ip;        // 指針變量的聲明
 
   ip = &var;       // 在指針變量中存儲 var 的位址
 
   cout << "Value of var variable: ";
   cout << var << endl;
 
   // 輸出在指針變量中存儲的位址
   cout << "Address stored in ip variable: ";
   cout << ip << endl;
 
   // 通路指針中位址的值
   cout << "Value of *ip variable: ";
   cout << *ip << endl;
 
   return 0;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

Value of var variable: 20

Address stored in ip variable: 0xbfc601ac

Value of *ip variable: 20

C++ 指針詳解

C++ Null 指針

C++ 支援空指針。NULL 指針是一個定義在标準庫中的值為零的常量。

在變量聲明的時候,如果沒有确切的位址可以指派,為指針變量賦一個 NULL 值是一個良好的程式設計習慣。

賦為 NULL 值的指針被稱為空指針。

NULL 指針是一個定義在标準庫中的值為零的常量。

請看下面的程式:

#include <iostream>

using namespace std;

int main ()
{
   int  *ptr = NULL;

   cout << "ptr 的值是 " << ptr ;
 
   return 0;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

ptr 的值是 0

(說明空指針變量中儲存的是0)

在大多數的作業系統上,程式不允許通路位址為 0 的記憶體,因為該記憶體是作業系統保留的。然而,記憶體位址 0 有特别重要的意義,它**表明該指針不指向一個可通路的記憶體位置。**但按照慣例,如果指針包含空值(零值),則假定它不指向任何東西。

如需檢查一個空指針,可以使用 if 語句,如下所示:

if(ptr)

是以,如果所有未使用的指針都被賦予空值,同時避免使用空指針,就可以防止誤用一個未初始化的指針。很多時候,未初始化的變量存有一些垃圾值,導緻程式難以調試。(一定要初始化變量)

define NULL 0 在vs2013中定義,0就是NULL的本質

C++ 指針的算術運算

可以對指針進行四種算術運算:++、–、+、-

指針是一個用數值表示的位址。是以,可以對指針執行算術運算。可以對指針進行四種算術運算:++、–、+、-。

假設一個指向位址1000的整型指針,是一個32位的整數,對指針進行算術運算: p++,它将指向1004,因為p每增加一次,都将指向下一個整數位置,就是後移四個位元組。

這個運算會在不影響記憶體位置中實際值的情況下,移動指針到下一個記憶體位置。如果 p指向一個位址為 1000 的字元,上面的運算會導緻指針指向位置 1001,因為下一個字元位置是在 1001。

遞增一個指針

可以在程式中用指針代替數組,因為變量指針可以遞增,而數組不能遞增,因為數組是一個常量指針。下面的程式遞增變量指針,以便順序通路數組中的每一個元素:

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int  *ptr;
 
   // 指針中的數組位址
   ptr = var;
   for (int i = 0; i < MAX; i++)
   {
      cout << "Address of var[" << i << "] = ";
      cout << ptr << endl;
 
      cout << "Value of var[" << i << "] = ";
      cout << *ptr << endl;
 
      // 移動到下一個位置
      ptr++;
   }
   return 0;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

Address of var[0] = 0xbfa088b0

Value of var[0] = 10

Address of var[1] = 0xbfa088b4

Value of var[1] = 100

Address of var[2] = 0xbfa088b8

Value of var[2] = 200

遞減一個指針

同樣地,對指針進行遞減運算,即把值減去其資料類型的位元組數,如下所示:

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int  *ptr;
 
   // 指針中最後一個元素的位址
   ptr = &var[MAX-1];
   for (int i = MAX; i > 0; i--)
   {
      cout << "Address of var[" << i << "] = ";
      cout << ptr << endl;
 
      cout << "Value of var[" << i << "] = ";
      cout << *ptr << endl;
 
      // 移動到下一個位置
      ptr--;
   }
   return 0;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

Address of var[3] = 0xbfdb70f8

Value of var[3] = 200

Address of var[2] = 0xbfdb70f4

Value of var[2] = 100

Address of var[1] = 0xbfdb70f0

Value of var[1] = 10

指針的比較

指針可以用關系運算符進行比較,如 ==、< 和 >。

如果 p1 和 p2 指向兩個相關的變量,比如同一個數組中的不同元素,則可對 p1 和 p2 進行大小比較。

下面的程式隻要變量指針所指向的位址小于或等于數組的最後一個元素的位址 &var[MAX - 1],則把變量指針進行遞增:

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int  *ptr;
 
   // 指針中第一個元素的位址
   ptr = var;
   int i = 0;
   while ( ptr <= &var[MAX - 1] )
   {
      cout << "Address of var[" << i << "] = ";
      cout << ptr << endl;
 
      cout << "Value of var[" << i << "] = ";
      cout << *ptr << endl;
 
      // 指向上一個位置
      ptr++;
      i++;
   }
   return 0;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

Address of var[0] = 0xbfce42d0

Value of var[0] = 10

Address of var[1] = 0xbfce42d4

Value of var[1] = 100

Address of var[2] = 0xbfce42d8

Value of var[2] = 200

C++ 指針 vs 數組

指針和數組之間有着密切的關系。

事實上,指針和數組在很多情況下是可以互換的。例如,一個指向數組開頭的指針,可以通過使用指針的算術運算或數組索引來通路數組。請看下面的程式:

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int  *ptr;
 
   // 指針中的數組位址
   ptr = var;
   for (int i = 0; i < MAX; i++)
   {
      cout << "var[" << i << "]的記憶體位址為 ";
      cout << ptr << endl;
 
      cout << "var[" << i << "] 的值為 ";
      cout << *ptr << endl;
 
      // 移動到下一個位置
      ptr++;
   }
   return 0;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

var[0]的記憶體位址為 0x7fff59707adc

var[0] 的值為 10

var[1]的記憶體位址為 0x7fff59707ae0

var[1] 的值為 100

var[2]的記憶體位址為 0x7fff59707ae4

var[2] 的值為 200

然而,指針和數組并不是完全互換的。例如,請看下面的程式:

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
 
   for (int i = 0; i < MAX; i++)
   {
      *var = i;    // 這是正确的文法
      var++;       // 這是不正确的
   }
   return 0;
}
           

把指針運算符 * 應用到 var 上是完全可以的,但修改 var 的值是非法的。這是因為 var 是一個指向數組開頭的常量,不能作為左值。

由于一個數組名對應一個指針常量,隻要不改變數組的值,仍然可以用指針形式的表達式。例如,下面是一個有效的語句,把 var[2] 指派為 500:

*(var + 2) = 500;

上面的語句是有效的,且能成功編譯,因為 var 未改變。

C++ 指針數組

可以定義用來存儲指針的數組。

執行個體,它用到了一個由 3 個整數組成的數組:

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
 
   for (int i = 0; i < MAX; i++)
   {
      cout << "Value of var[" << i << "] = ";
      cout << var[i] << endl;
   }
   return 0;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

Value of var[0] = 10

Value of var[1] = 100

Value of var[2] = 200

可能有一種情況,我們想要讓數組存儲指向 int 或 char 或其他資料類型的指針。下面是一個指向整數的指針數組的聲明:

int *ptr[MAX];

在這裡,把 ptr 聲明為一個數組,由 MAX 個整數指針組成。是以,ptr 中的每個元素,都是一個指向 int 值的指針。下面的執行個體用到了三個整數,它們将存儲在一個指針數組中,如下所示:

#include <iostream>
 
using namespace std;
const int MAX = 3;
 
int main ()
{
   int  var[MAX] = {10, 100, 200};
   int *ptr[MAX];
 
   for (int i = 0; i < MAX; i++)
   {
      ptr[i] = &var[i]; // 指派為整數的位址
   }
   for (int i = 0; i < MAX; i++)
   {
      cout << "Value of var[" << i << "] = ";
      cout << *ptr[i] << endl;
   }
   return 0;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

Value of var[0] = 10

Value of var[1] = 100

Value of var[2] = 200

也可以用一個指向字元的指針數組來存儲一個字元串清單,如下:

#include <iostream>
 
using namespace std;
const int MAX = 4;
 
int main ()
{
 const char *names[MAX] = {
                   "Zara Ali",
                   "Hina Ali",
                   "Nuha Ali",
                   "Sara Ali",
   };
 
   for (int i = 0; i < MAX; i++)
   {
      cout << "Value of names[" << i << "] = ";
      cout << names[i] << endl;
   }
   return 0;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

Value of names[0] = Zara Ali

Value of names[1] = Hina Ali

Value of names[2] = Nuha Ali

Value of names[3] = Sara Ali

C++ 指向指針的指針

C++ 允許指向指針的指針。

指向指針的指針是一種多級間接尋址的形式,或者說是一個指針鍊。

指針的指針就是将指針的位址存放在另一個指針裡面。

通常,一個指針包含一個變量的位址。當我們定義一個指向指針的指針時,第一個指針包含了第二個指針的位址,第二個指針指向包含實際值的位置。

一個指向指針的指針變量必須如下聲明,即在變量名前放置兩個星号。例如,下面聲明了一個指向 int 類型指針的指針:

int **var;

當一個目标值被一個指針間接指向到另一個指針時,通路這個值需要使用兩個星号運算符

#include <iostream>
 
using namespace std;
 
int main ()
{
    int  var;
    int  *ptr;
    int  **pptr;
 
    var = 3000;
 
    // 擷取 var 的位址
    ptr = &var;
 
    // 使用運算符 & 擷取 ptr 的位址
    pptr = &ptr;
 
    // 使用 pptr 擷取值
    cout << "var 值為 :" << var << endl;
    cout << "*ptr 值為:" << *ptr << endl;
    cout << "**pptr 值為:" << **pptr << endl;
 
    return 0;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

var 值為 :3000

*ptr 值為:3000

**pptr 值為:3000

(解引用兩次,從指針位址找到第一個指針内部,在通過這個内部儲存的位址值找到原始的值)

C++ 傳遞指針給函數

通過引用或位址傳遞參數,使傳遞的參數在調用函數中被改變。

C++ 允許傳遞指針給函數,隻需要簡單地聲明函數參數為指針類型即可。

下面的執行個體中,我們傳遞一個無符号的 long 型指針給函數,并在函數内改變這個值:

#include <iostream>
#include <ctime>
 
using namespace std;
 
// 在寫函數時應習慣性的先聲明函數,然後在定義函數
void getSeconds(unsigned long *par);
 
int main ()
{
   unsigned long sec;
 
 
   getSeconds( &sec );
 
   // 輸出實際值
   cout << "Number of seconds :" << sec << endl;
 
   return 0;
}
 
void getSeconds(unsigned long *par)
{
   // 擷取目前的秒數
   *par = time( NULL );
   return;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

Number of seconds :1294450468

(把位址傳遞給函數,函數的參數指向這個位址,解引用其中的值,然後給他指派,最後輸出這個值)

能接受指針作為參數的函數,也能接受數組作為參數,如下所示:

#include <iostream>
using namespace std;
 
// 函數聲明
double getAverage(int *arr, int size);
 
int main ()
{
   // 帶有 5 個元素的整型數組
   int balance[5] = {1000, 2, 3, 17, 50};
   double avg;
 
   // 傳遞一個指向數組的指針作為參數
   avg = getAverage( balance, 5 ) ;
 
   // 輸出傳回值
   cout << "Average value is: " << avg << endl; 
    
   return 0;
}
 
double getAverage(int *arr, int size)
{
  int    i, sum = 0;       
  double avg;          
 
  for (i = 0; i < size; ++i)
  {
    sum += arr[i];
   }
 
  avg = double(sum) / size;
 
  return avg;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

Average value is: 214.4

C++ 從函數傳回指針

C++ 允許函數傳回指針到局部變量、靜态變量和動态記憶體配置設定。

我們已經了解了 C++ 中如何從函數傳回數組,類似地,C++ 允許從函數傳回指針。為了做到這點,必須聲明一個傳回指針的函數,如下所示:

int * myFunction()

{

.

.

.

}

另外,C++ 不支援在函數外傳回局部變量的位址,除非定義局部變量為 static變量。

現在,讓我們來看下面的函數,它會生成 10 個随機數,并使用表示指針的數組名(即第一個數組元素的位址)來傳回它們,具體如下:

#include <iostream>
#include <ctime>
#include <cstdlib>
 
using namespace std;
 
// 要生成和傳回随機數的函數
int * getRandom( )
{
  static int  r[10];
 
  // 設定種子
  srand( (unsigned)time( NULL ) );
  for (int i = 0; i < 10; ++i)
  {
    r[i] = rand();
    cout << r[i] << endl;
  }
 
  return r;
}
 
// 要調用上面定義函數的主函數
int main ()
{
   // 一個指向整數的指針
   int *p;
 
   p = getRandom();
   for ( int i = 0; i < 10; i++ )
   {
       cout << "*(p + " << i << ") : ";
       cout << *(p + i) << endl;
   }
 
   return 0;
}
           

當上面的代碼被編譯和執行時,它會産生下列結果:

624723190

1468735695

807113585

976495677

613357504

1377296355

1530315259

1778906708

1820354158

667126415

*(p + 0) : 624723190

*(p + 1) : 1468735695

*(p + 2) : 807113585

*(p + 3) : 976495677

*(p + 4) : 613357504

*(p + 5) : 1377296355

*(p + 6) : 1530315259

*(p + 7) : 1778906708

*(p + 8) : 1820354158

*(p + 9) : 667126415

繼續閱讀