포인터 관련 연산자 (C# 참조)

  • 13 minutes to read
NOTE

현재 문서는 다음 경로의 Microsoft Learn 자료를 번역했습니다. Pointer related operators (C# reference). 이 문서에 대한 모든 저작권은 마이크로소프트에 있으며 요청이 있을 경우 언제라도 게시가 중단될 수 있습니다. 번역 내용에 오역이 존재할 수 있고 주석은 번역자 개인의 의견일 뿐이며 마이크로소프트는 이에 관한 어떠한 보장도 하지 않습니다. 번역이 완료된 이후에도 대상 제품 및 기술이 개선되거나 변경됨에 따라 원문의 내용도 변경되거나 보완되었을 수 있으므로 주의하시기 바랍니다.

다음 연산자를 사용하여 포인터로 작업할 수 있습니다.

  • 단항 &(address-of) 연산자: 변수의 주소를 가져옵니다.
  • 단항 * (포인터 간접 참조) 연산자: 포인터가 가리키는 변수(변숫값)를 가져옵니다.
  • -> (멤버 액세스) 및 [] (요소 액세스) 연산자
  • 산술 연산자: +, -, ++--
  • 비교 연산자: ==, !=, <, >, <=>=

포인터 형식에 대한 내용은 포인터 형식을 참조하세요.

NOTE

포인터를 사용한 작업에는 안전하지 않은 컨텍스트가 필요합니다. 안전하지 않은 블록을 포함하는 코드는 AllowUnsafeBlocks 컴파일러 옵션을 사용하여 컴파일해야 합니다.

Address-of 연산자 &

단항 & 연산자는 해당 피연산자의 주소를 반환합니다.

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 문으로 "fix" 또는 "pin"으로 할 경우 이동 가능한 변수의 주소를 가져올 수 있습니다. 가져온 주소는 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.
    }
}

변수, 배열과 달리 상수 또는 리터럴 값의 주소를 가져올 수 없습니다.

포인터 간접 참조 연산자 *

단항 포인터 간접 참조 연산자 *는 피연산자가 가리키는 변수를 가져옵니다. 역참조 연산자라고도 합니다. * 연산자의 피연산자는 포인터 형식이여야 합니다.

PointerIndirection.cs

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* 유형의 식에 적용할 수 없습니다.

산술 연산자 학습을 통해서 알고 있는 단항 연산자가 아닌 이항 연산자로서 사용되는 * 연산자는 해당 숫자 피연산자의 곱을 컴퓨팅합니다.

포인터 멤버 액세스 연산자 ->

화살표 연산자로 불리는 -> 연산자는 포인터 간접 참조멤버 액세스를 결합합니다. 즉, xT* 형식의 포인터이고 yT의 액세스 가능한 멤버인 경우 다음과 같이 사용될 수 있습니다.

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)
    }
}
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)
    }
}

You cannot apply the -> operator to an expression of type void*.

-> 연산자를 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
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 식은 스택에 메모리 블록을 할당합니다.

NOTE

포인터 요소 액세스 연산자는 범위 이탈 오류를 검사하지 않습니다.

void* 형식의 식으로 포인터 요소 액세스에 [] 형태를 사용할 수 없습니다.

배열 요소 또는 인덱서 액세스에 대해 [] 연산자를 사용할 수도 있습니다.

포인터 산술 연산자

포인터를 사용하여 다음과 같은 산술 연산을 수행할 수 있습니다.

  • 포인터에서 정수 값 추가 또는 빼기
  • 두 포인터 빼기
  • 포인터 증가 또는 감소

void* 형식의 포인터를 사용하여 이러한 작업을 수행할 수 없습니다.

포인터에서 정수 값 더하기 또는 빼기

T* 형식의 포인터 pint, uint, long 또는 ulong으로 암시적으로 변환할 수 있는 형식의 n 식에 대한 더하기와 빼기가 다음과 같이 정의됩니다.

  • p + nn + p 식 모두 p에서 지정한 주소에 n * sizeof(T)를 추가한 결과 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
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* 형식의 두 가지 포인터 p1p2에 대해 p1 - p2 식은 p1p2에 의해 지정된 주소 간의 차이를 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
}
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는 해당 라인에서의 실행 이후의 값입니다. 이 내용은 우리가 앞서 증감 연산자 파트에서 학습한 내용과 동일합니다.

The following example demonstrates the behavior of both postfix and prefix increment operators: 다음 예제에서는 접미사 및 접두사 증가 연산자의 동작을 보여줍니다.

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
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 연산자와 &* 연산자
  • 가감 연산자인 +- 연산자
  • 비교 연산자인 <, >, <=>= 연산자
  • 같음 ==!= 연산자

괄호 연산자인 ()를 사용하여 연산자 우선순위에 따라 주어진 평가 순서를 변경합니다.

연산자 오버로드 가능성

사용자 정의 형식은 포인터 관련 연산자 &, *, ->[](을)를 오버로드할 수 없습니다.

참고 항목

현재 문서는 다음 링크의 자료를 번역하였습니다.

Pointer related operators (C# reference)

VisualAcademy Docs의 모든 콘텐츠, 이미지, 동영상의 저작권은 박용준에게 있습니다. 저작권법에 의해 보호를 받는 저작물이므로 무단 전재와 복제를 금합니다. 사이트의 콘텐츠를 복제하여 블로그, 웹사이트 등에 게시할 수 없습니다. 단, 링크와 SNS 공유, Youtube 동영상 공유는 허용합니다. www.VisualAcademy.com
박용준 강사의 모든 동영상 강의는 데브렉에서 독점으로 제공됩니다. www.devlec.com