指標相關運算子 - 取得變數的位址、對儲存位置取值,以及存取記憶體位置
指標運算子可讓您取得變數位址 (&
)、對指標取值 (*
)、比較指標值,以及加入或減去指標和整數。
您可以搭配下列運算子來使用指標:
- 一元
&
(傳址) 運算子:取得變數位址 - 一元
*
(指標間接) 運算子:取得指標指向的變數 ->
(成員存取) 和[]
(元素存取) 運算子- 算術運算子
+
、-
、++
和--
- 比較運算子
==
、!=
、<
、>
、<=
和>=
如需指標型別的資訊,請參閱指標型別。
注意
任何具有指標的作業都需要 unsafe 內容。 包含不安全區塊的程式碼必須使用 AllowUnsafeBlocks 編譯器選項進行編譯。
傳址運算子 &
一元 &
運算子會傳回其運算元的位址:
unsafe
{
int number = 27;
int* pointerToNumber = &number;
Console.WriteLine($"Value of the variable: {number}");
Console.WriteLine($"Address of the variable: {(long)pointerToNumber:X}");
}
// Output is similar to:
// Value of the variable: 27
// Address of the variable: 6C1457DBD4
&
運算子的運算元必須是固定的變數。 「固定」變數是位在不受記憶體回收行程作業影響之儲存位置的變數。 在前述範例中,區域變數 number
是固定的變數,因為它位於堆疊上。 會受到記憶體回收行程影響且位在儲存位置的變數 (例如重新配置),稱為「可移動」變數。 物件欄位和陣列元素是可移動變數的範例。 如果以 fixed
陳述式來「修正」或「釘選」它,則您可取得可移動變數的位址。 取得的位址只有在 fixed
陳述式的區塊內才有效。 下例顯示如何使用 fixed
陳述式和 &
運算子:
unsafe
{
byte[] bytes = { 1, 2, 3 };
fixed (byte* pointerToFirst = &bytes[0])
{
// The address stored in pointerToFirst
// is valid only inside this fixed statement block.
}
}
您無法取得常數或值的位址。
如需固定和可移動變數的詳細資訊,請參閱 C# 語言規格的固定和可移動變數一節。
二元 &
運算子會計算其布林運算元的邏輯 AND或其整數運算元的位元邏輯 AND。
指標間接運算子 *
一元指標間接運算子 *
取得其運算元指向的變數。 它也稱之為取值運算子。 *
運算子的運算元必須是指標型別。
unsafe
{
char letter = 'A';
char* pointerToLetter = &letter;
Console.WriteLine($"Value of the `letter` variable: {letter}");
Console.WriteLine($"Address of the `letter` variable: {(long)pointerToLetter:X}");
*pointerToLetter = 'Z';
Console.WriteLine($"Value of the `letter` variable after update: {letter}");
}
// Output is similar to:
// Value of the `letter` variable: A
// Address of the `letter` variable: DCB977DDF4
// Value of the `letter` variable after update: Z
您不能將 *
運算子套用至 void*
型別的運算式。
二元 *
運算子會計算其數值運算元的乘積。
指標成員存取運算子 ->
->
運算子結合指標間接和成員存取。 亦即,如果 x
是 T*
型別的指標,而 y
是 T
型別的可存取成員,則運算式格式為
x->y
相當於
(*x).y
下列範例示範 ->
運算子的用法:
public struct Coords
{
public int X;
public int Y;
public override string ToString() => $"({X}, {Y})";
}
public class PointerMemberAccessExample
{
public static unsafe void Main()
{
Coords coords;
Coords* p = &coords;
p->X = 3;
p->Y = 4;
Console.WriteLine(p->ToString()); // output: (3, 4)
}
}
您不能將 ->
運算子套用至 void*
型別的運算式。
指標元素存取運算子 []
針對指標型別的運算式 p
,p[n]
格式的指標元素存取會評估為 *(p + n)
,其中 n
必須是可隱含轉換成 int
、uint
、long
或 ulong
的型別。 如需 +
運算子搭配指標的行為資訊,請參閱在指標中加減整數值一節。
下例示範如何使用指標和 []
運算子存取陣列元素:
unsafe
{
char* pointerToChars = stackalloc char[123];
for (int i = 65; i < 123; i++)
{
pointerToChars[i] = (char)i;
}
Console.Write("Uppercase letters: ");
for (int i = 65; i < 91; i++)
{
Console.Write(pointerToChars[i]);
}
}
// Output:
// Uppercase letters: ABCDEFGHIJKLMNOPQRSTUVWXYZ
在上述範例中,stackalloc
運算式會在堆疊上配置記憶體區塊。
注意
指標元素存取運算子不會檢查超出範圍的錯誤。
型別為 void*
的運算式,指標元素存取無法使用 []
。
陣列元素或索引子存取也可以使用 []
運算子。
指標算術運算子
您可以使用指標執行下列算術運算:
- 在指標中加減整數值
- 減去兩個指標
- 遞增或遞減指標
您無法使用 void*
型別的指標執行這些作業。
如需支援的數值型別算術運算資訊,請參閱算術運算子。
在指標中加減整數值
針對 T*
型別的 p
指標和可隱含轉換成 int
、uint
、long
或 ulong
的運算式 n
,加減定義如下:
p + n
和n + p
運算式都會產生因為將n * sizeof(T)
新增到p
指定位址而得到的T*
型別指標。p - n
運算式會產生因為從p
指定的位址減去n * sizeof(T)
而得到的T*
型別指標。
sizeof
運算子取得以位元組為單位的型別大小。
下例示範 +
運算子加指標的用法:
unsafe
{
const int Count = 3;
int[] numbers = new int[Count] { 10, 20, 30 };
fixed (int* pointerToFirst = &numbers[0])
{
int* pointerToLast = pointerToFirst + (Count - 1);
Console.WriteLine($"Value {*pointerToFirst} at address {(long)pointerToFirst}");
Console.WriteLine($"Value {*pointerToLast} at address {(long)pointerToLast}");
}
}
// Output is similar to:
// Value 10 at address 1818345918136
// Value 30 at address 1818345918144
指標減法
針對 T*
型別的兩個指標 p1
和 p2
,運算式 p1 - p2
會產生 p1
和 p2
除以 sizeof(T)
所指定的位址間差異。 結果的類型是 long
。 亦即,p1 - p2
會計算為 ((long)(p1) - (long)(p2)) / sizeof(T)
。
下例示範指標減法:
unsafe
{
int* numbers = stackalloc int[] { 0, 1, 2, 3, 4, 5 };
int* p1 = &numbers[1];
int* p2 = &numbers[5];
Console.WriteLine(p2 - p1); // output: 4
}
指標遞增和遞減
++
遞增運算子在其指標運算元中加 1。 --
遞減運算子從其指標運算元中減 1。
這兩個運算子都支援兩種形式:後置 (p++
和 p--
) 及前置 (++p
和 --p
)。 p++
和 p--
的結果是運算「前」的 p
值。 ++p
和 --p
的結果是運算「後」的 p
值。
下例示範前置和後置遞增運算子的行為:
unsafe
{
int* numbers = stackalloc int[] { 0, 1, 2 };
int* p1 = &numbers[0];
int* p2 = p1;
Console.WriteLine($"Before operation: p1 - {(long)p1}, p2 - {(long)p2}");
Console.WriteLine($"Postfix increment of p1: {(long)(p1++)}");
Console.WriteLine($"Prefix increment of p2: {(long)(++p2)}");
Console.WriteLine($"After operation: p1 - {(long)p1}, p2 - {(long)p2}");
}
// Output is similar to
// Before operation: p1 - 816489946512, p2 - 816489946512
// Postfix increment of p1: 816489946512
// Prefix increment of p2: 816489946516
// After operation: p1 - 816489946516, p2 - 816489946516
指標比較運算子
您可以使用 ==
、!=
、<
、>
、<=
和 >=
運算子來比較包括 void*
在內之任何指標型別的運算元。 這些運算子會比較兩個運算元指定的位址,如同它們是不帶正負號的整數一樣。
如需這些其他型別運算元的運算子行為資訊,請參閱等號運算子和比較運算子文章。
運算子優先順序
下列清單排列指標相關的運算子,從最高優先順序到最低:
- 後置遞增
x++
和遞減x--
運算子以及->
和[]
運算子 - 前置遞增
++x
和遞減--x
運算子以及&
和*
運算子 - 加法類
+
和-
運算子 - 比較
<
、>
、<=
和>=
運算子 - 等號
==
和!=
運算子
使用括弧 ()
變更由運算子優先順序強制執行的評估順序。
如需依優先順序層級排序的 C# 運算子完整清單,請參閱 C# 運算子一文的運算子優先順序一節。
運算子是否可多載
使用者定義的型別無法多載指標相關運算子 &
、*
、->
和 []
。
C# 語言規格
如需詳細資訊,請參閱 C# 語言規格的下列幾節: