C++ 程式設計/運算子/指標
* 運算子用於宣告指標型別,但也用於獲取指標指向的變數。
指標是重要的資料型別,因為它們具有特殊的特性。它們可用於指示變數,而無需實際建立該型別的變數。由於指標可能難以理解,因此應花一些精力來理解它們賦予程式設計師的強大功能。
指標具有非常描述性的名稱。指標變數只儲存記憶體地址,通常是其他變數的地址。本質上,它們指向另一個變數的記憶體位置,即計算機記憶體上的一個保留位置。可以使用指標 將變數的位置傳遞給函式,這使得函式的指標可以使用變數空間,以便它可以檢索或修改其資料。你甚至可以擁有指向指標的指標,以及指向指向指標的指標等等。
透過在宣告中的變數名前新增一個*來宣告指標,如下例所示
int* x; // pointer to int.
int * y; // pointer to int. (legal, but rarely used)
int *z; // pointer to int.
int*i; // pointer to int. (legal, but rarely used)
注意,* 僅與以下宣告相關聯
int* i, j; // CAUTION! i is pointer to int, j is int.
int *i, *j; // i and j are both pointer to int.
你還可以將多個指標連結在一起,如下例所示
int **i; // Pointer to pointer to int.
int ***i; // Pointer to pointer to pointer to int (rarely used).
每個人都會對指標感到困惑,因為將值賦給指標可能有點棘手,但如果你知道基礎知識,你可以更容易地進行操作。透過仔細閱讀示例,而不是簡單的描述,嘗試理解所呈現給你的要點。
- 將值賦給指標(非字元型別)
double vValue = 25.0;// declares and initializes a vValue as type double
double* pValue = &vValue;
cout << *pValue << endl;
第二條語句使用 “&” 引用運算子和 "*" 來告訴編譯器這是一個指標變數,並將 vValue 變數的地址賦給它。在最後一條語句中,它透過使用 "*" 運算子解引用指標來輸出 vValue 變數的值。
- 將值賦給指標(字元型別)
char pArray[20] = {"Name1"};
char* pValue(pArray);// or 0 in old compilers, nullptr is a part of C++0X
pValue = "Value1";
cout << pValue << endl ;// this will return the Value1;
正如前面提到的,指標是一個儲存另一個變數地址的變數,因此你需要初始化陣列,因為你不能直接將值賦給它。你需要使用指標直接或在混合上下文中使用指向陣列的指標,為了單獨使用指標,請檢視下一個示例。
char* pValue("String1");
pValue = "String2";
cout << pValue << endl ;
請記住,你不能讓指標單獨存在或將其初始化為 nullptr,因為這會導致錯誤。編譯器認為它是一個記憶體地址持有者變數,因為你沒有指向任何東西,並且會嘗試將值賦給它,這會導致錯誤,因為它沒有指向任何地方。
這是*運算子。它用於獲取指標指向的變數。它也用於宣告指標型別。
當你擁有一個指標時,你需要某種方法來訪問它指向的記憶體。當它放在指標前面時,它會給出它指向的變數。這是一個左值,因此你可以將值賦給它,甚至可以從它初始化一個引用。
#include <iostream>
int main()
{
int i;
int * p = &i;
i = 3;
std::cout<<*p<<std::endl; // prints "3"
return 0;
}
由於&運算子的結果是一個指標,*&i是有效的,儘管它沒有任何效果。
現在,當你將*運算子與類結合使用時,你可能會注意到一個問題。它的優先順序低於.!請參見示例
struct A { int num; };
A a;
int i;
A * p;
p = &a;
a.num = 2;
i = *p.num; // Error! "p" isn't a class, so you can't use "."
i = (*p).num;
錯誤發生是因為編譯器首先檢視 p.num(“.” 的優先順序高於 “*”),並且因為 p 沒有名為 num 的成員,所以編譯器會向你顯示錯誤。使用分組符號來更改優先順序可以解決此問題。
如果必須編寫(*p).num很多次,尤其是當你有很多類時,這將非常耗時。想象一下編寫(*(*(*(*MyPointer).Member).SubMember).Value).WhatIWant!因此,存在一個特殊的運算子,->。而不是(*p).num,你可以編寫p->num,它在所有方面都是完全相同的。現在你可以編寫MyPointer->Member->SubMember->Value->WhatIWant。這對大腦來說容易得多!
空指標是指標的一種特殊狀態。這意味著指標不指向任何東西。嘗試解引用(使用*或->運算子)空指標是錯誤的。可以使用常量零引用空指標,如下例所示
int i;
int *p;
p = 0; //Null pointer.
p = &i; //Not the null pointer.
請注意,你不能將指標賦給整數,即使是零。它必須是常量。以下程式碼是錯誤的
int i = 0;
int *p = i; //Error: 0 only evaluates to null if it's a pointer
有一箇舊的宏,在標準庫中定義,它源自 C 語言,並且不一致地發展為 #define NULL ((void *)0),這使得NULL始終等於空指標值(本質上是 0)。
由於空指標是 0,因此它始終與 0 進行比較。就像整數一樣,如果你在真/假表示式中使用它,如果它是空指標,它將返回假,如果它是非空指標,則返回真
#include <iostream>
void IsNull (int * p)
{
if (p)
std::cout<<"Pointer is not NULL"<<std::endl;
else
std::cout<<"Pointer is NULL"<<std::endl;
}
int main()
{
int * p;
int i;
p = NULL;
IsNull(p);
p = &i;
IsNull(&i);
IsNull(p);
IsNull(NULL);
return 0;
}
此程式將輸出指標為 NULL,然後輸出兩次指標不為 NULL,然後再次輸出指標為 NULL。
- 指標和多維非字元陣列
需要了解如何初始化二維陣列、將值賦給陣列以及從陣列返回值。有關陣列的詳細資訊,請參見 1.4.10.1.1 陣列 部分。但是,在與指標理解相關的部分,陣列也會在此處提及。
- 主要物件是
- 將值賦給多維指標
- 如何在多維陣列中使用指標
- 返回值
- 初始化指標和陣列
- 如何安排它們的值
- 將值賦給多維指標。
在非字元型別中,你需要使用陣列和指標,因為指標以特殊方式處理 char* 型別,而以其他方式處理其他型別,例如,僅引用地址或獲取地址並透過間接方法獲取值。
如果你以這種方式宣告它
double (*pDVal)[2] = {{1,2},{1,2}};
這可能會產生錯誤!因為非字元型別中使用的指標僅直接使用,而在字元型別中,透過首先分配一個變數來引用另一個變數的地址,然後你可以間接獲取它的(已分配變數的)值!
double ArrayVal[5][5] = {
{1,2,3,4,5},
{1,2,3,4,5},
{1,2,3,4,5},
{1,2,3,4,5},
{1,2,3,4,5},
};
double(*pArray)[5] = ArrayVal;
*(*(pArray+0)+0) = 10;
*(*(pArray+0)+1) = 20;
*(*(pArray+0)+2) = 30;
*(*(pArray+0)+3) = 40;
*(*(pArray+0)+4) = 50;
*(*(pArray+1)+0) = 60;
*(*(pArray+1)+1) = 70;
*(*(pArray+1)+2) = 80;
*(*(pArray+1)+3) = 90;
*(*(pArray+1)+4) = 100;
*(*(pArray+2)+0) = 110;
*(*(pArray+2)+1) = 120;
*(*(pArray+2)+2) = 130;
*(*(pArray+2)+3) = 140;
*(*(pArray+2)+4) = 150;
*(*(pArray+3)+0) = 160;
*(*(pArray+3)+1) = 170;
*(*(pArray+3)+2) = 180;
*(*(pArray+3)+3) = 190;
*(*(pArray+3)+4) = 200;
*(*(pArray+4)+0) = 210;
*(*(pArray+4)+1) = 220;
*(*(pArray+4)+2) = 230;
*(*(pArray+4)+3) = 240;
*(*(pArray+4)+4) = 250;
還有另一種方法,而不是
*(*(pArray+0)+0)
它是
*(pArray[0]+0)
你可以使用其中之一透過指標將值賦給陣列,為了返回結果,可以使用適當的陣列或指標。
- 指標和多維字元陣列
這有點難,甚至很難記住,所以我建議你不斷練習,直到你掌握指標的精神!你不能將指標 + 多維陣列與字元型別一起使用。僅用於非字元型別。
- 具有字元型別的多維指標
char* pVar[5] = { "Name1" , "Name2" , "Name3", "Name4", "Name5" }
pVar[0] = "XName01";
cout << pVar[0] << endl ; //this will return the XName01 instead Name1 which was replaced with Name1.
這裡第一個語句中的 5 是行數(指標中不需要指定列數,只有在陣列中),下一個語句將另一個字串賦給位置 0,即第一個語句的第一個位置。最後返回結果。
- 動態記憶體分配
在你的系統記憶體中,每個記憶體塊都有一個地址,因此當你編譯程式碼時,所有變數在開始時都會在記憶體中保留一些空間,但在動態記憶體分配中,只有在需要時才會保留,這意味著在該語句的執行時間,它會在你的空閒空間區域(未使用空間)中分配記憶體,因此這意味著如果沒有空間或沒有連續的塊,編譯器將生成錯誤訊息。
- 動態記憶體分配和非字元型別指標
這與將非字元一維陣列賦給指標相同
double* pVal = new double[5];
//or double* pVal = new double; // this line leaves out the necessary memory allocation
*(pVal+0) = 10;
*(pVal+1) = 20;
*(pVal+2) = 30;
*(pVal+3) = 40;
*(pVal+4) = 50;
cout << *(pVal+0) << endl;
第一條語句的 Lside(左側)聲明瞭一個變數,Rside 請求為雙精度型變數分配空間,並在您的記憶體中的空閒空間區域分配它。因此,下一個和接下來的語句,您可以看到它增加了整數值,這意味著 *(pVal+0) pVal -> 如果單獨使用它將返回對應於第一個記憶體塊的地址。(用於儲存 10)並且 0 表示前進 0 個塊,但它的 0 表示不要移動,停留在當前記憶體塊中,並且您使用 () 括號,因為 + < * < () 考慮優先順序,因此您需要使用括號避免先計算 *
- 稱為間接運算子,它解引用指標並返回與記憶體塊相對應的值。
(記憶體塊地址+步長)
- -> 解引用。
- 動態記憶體分配和指標字元型別
char* pVal = new char;
pVal = "Name1";
cout << pVal << endl;
delete pVal; //this will delete the allocated space
pVal = nullptr //null the pointer
您可以看到這與靜態記憶體宣告相同,在靜態宣告中它會
char* pVal("Name1");
- 動態記憶體分配和指標非字元陣列型別
double (*pVal2)[2]= new double[2][2]; //this will add 2x2 memory blocks to type double pointer
*(*(pVal2+0)+0) = 10;
*(*(pVal2+0)+1) = 10;
*(*(pVal2+0)+2) = 10;
*(*(pVal2+0)+3) = 10;
*(*(pVal2+0)+4) = 10;
*(*(pVal2+1)+0) = 10;
*(*(pVal2+1)+1) = 10;
*(*(pVal2+1)+2) = 10;
*(*(pVal2+1)+3) = 10;
*(*(pVal2+1)+4) = 10;
delete [] pVal; //the dimension does not matter; you only need to mention []
pVal = nullptr
此指標間接運算子用於訪問類指標的成員。
此指向成員的解引用運算子用於訪問與特定類例項關聯的變數,前提是提供了相應的指標。
此指向成員的間接運算子用於訪問與一個指標指向的類例項關聯的變數,前提是提供了另一個合適的指向成員的指標。
當用於指向函式時,指標可以非常強大。可以在程式中的任何位置呼叫函式,只需要知道它接受的引數型別。 指向函式的指標 在標準庫中被多次使用,併為其他需要適應任何使用者程式碼的庫提供了強大的系統。本例將在 函式部分 中進行更深入的探討。
