共用方式為


指標相關運算子 - 取得變數的位址、對儲存位置取值,以及存取記憶體位置

指標運算子可讓您取得變數位址 (&)、對指標取值 (*)、比較指標值,以及加入或減去指標和整數。

C# 語言參考資料記錄了 C# 語言最新版本。 同時也包含即將推出語言版本公開預覽功能的初步文件。

文件中標示了語言最近三個版本或目前公開預覽版中首次引入的任何功能。

小提示

欲查詢某功能何時首次在 C# 中引入,請參閱 C# 語言版本歷史的條目。

請使用以下運算子來操作指標:

如需指標型別的資訊,請參閱指標型別

注意

任何具有指標的作業都需要 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 必須是可隱式轉換為 intuintlongulong的類型。 如需 + 運算子搭配指標的行為資訊,請參閱在指標中加減整數值一節。

以下範例示範如何利用指標和 [] 運算子存取陣列元素:

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,以及一個可隱式轉換為 intuintlong、 或 ulong的表達n式,加法與減法的運算方式如下:

  • 兩者都p + nn + p給你一個類型的T*指標。 你透過加到 n * sizeof(T) 指向 p 的地址來取得這個指標。
  • 這個表達式 p - n 會給你一個型 T*別為 的指標。 你透過從指向p的位址減n * sizeof(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

指標減法

對於兩個指標 和 p2 ,型別T*為 ,該表達p1 - p2式給出 與 p1p2 所指向位址的差值除以 sizeof(T)p1 其結果會是 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# 語言規格的下列幾節:

另請參閱