포인터 연산자를 사용하면 변수(&)의 주소를 가져오고, 포인터(*)를 역참조하고, 포인터 값을 비교하고, 포인터와 정수를 더하거나 뺄 수 있습니다.
C# 언어 참조는 가장 최근에 릴리스된 C# 언어 버전을 문서화합니다. 또한 예정된 언어 릴리스의 공개 미리 보기 기능에 대한 초기 설명서도 포함되어 있습니다.
설명서는 언어의 마지막 세 버전 또는 현재 공개 미리 보기에서 처음 도입된 기능을 식별합니다.
팁 (조언)
C#에서 기능이 처음 도입된 시기를 찾으려면 C# 언어 버전 기록에 대한 문서를 참조하세요.
다음 연산자를 사용하여 포인터를 사용합니다.
- 단항
&(address-of) 연산자: 변수의 주소를 가져오려면 - 단항
*(포인터 간접 참조) 연산자: 포인터가 가리키는 변수를 가져옵니다. -
->(멤버 액세스) 및[](요소 액세스) 연산자 - 산술 연산자
+,-,++및-- - 비교 연산자
==,!=,<,>,<=및>=
포인터 형식에 대한 내용은 포인터 형식을 참조하세요.
참고 항목
포인터를 사용한 작업에는 안전하지 않은 컨텍스트가 필요합니다. 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* 형식의 식에 적용할 수 없습니다.
이진 * 연산자는 해당 숫자 피연산자의 곱을 컴퓨팅합니다.
포인터 멤버 액세스 연산자 ->
-> 연산자는 포인터 간접 참조와 멤버 액세스를 결합합니다. 형식의 포인터이고 y 형식 T* 의 T액세스 가능한 멤버인 경우 x 양식의 식입니다.
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 은 암시적으로 , uint또는 longulong.로 변환할 수 있는 int형식이어야 합니다. 포인터가 있는 + 연산자의 동작에 대한 자세한 내용은 포인터에 또는 포인터에서 정수 값 더하기 또는 빼기 섹션을 참조하세요.
다음 예제에서는 포인터 및 연산자를 사용하여 배열 요소에 액세스하는 [] 방법을 보여 줍니다.
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* 형식의 포인터를 사용하여 이러한 작업을 수행할 수 없습니다.
숫자 형식을 사용하여 지원되는 산술 연산에 대한 자세한 내용은 산술 연산자를 참조하세요.
포인터로 또는 포인터에서 정수 값 추가 또는 빼기
형식의 포인터 p 및 형식의 식 n 은 다음과 같이 암시적으로 , uintlong또는 ulong추가 및 빼기 작업으로 변환할 수 int있습니다.T*
- 둘 다
p + n및n + p형식T*의 포인터를 제공합니다. 가리키는 주소p에 추가하여n * sizeof(T)이 포인터를 가져옵니다. - 식은
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
포인터 빼기
두 포인터와 형식의 경우 식 p1 - p2 은 가리키는 주소와 p2 가리키는 주소 p1 의 차이를 구분하여 sizeof(T)제공합니다.T*p2p1 결과는 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# 언어 사양의 다음 섹션을 참조하세요.
참고 항목
.NET