수색…
비고
포인터 및 unsafe
포인터의 성격으로 인해 포인터는 검증 할 수없는 코드를 생성합니다. 따라서 모든 포인터 유형을 사용하려면 unsafe
컨텍스트가 필요합니다.
System.IntPtr
형식은 void*
주위의 안전한 래퍼입니다. 당면 과제를 수행하기 위해 안전하지 않은 컨텍스트가 필요하지 않은 경우 void*
대한보다 편리한 대안으로 사용됩니다.
정의되지 않은 동작
C 및 C ++ 에서처럼 포인터의 잘못된 사용은 정의되지 않은 동작을 호출 할 수 있으며 가능한 부작용은 메모리 손상 및 의도하지 않은 코드 실행입니다. 대부분의 포인터 연산의 검증 할 수없는 특성으로 인해 포인터의 올바른 사용은 전적으로 프로그래머의 책임입니다.
포인터를 지원하는 유형
C 및 C ++과 달리 모든 C # 유형에는 해당 포인터 유형이 없습니다. 다음 두 조건이 모두 적용되면 형식 T
는 해당 포인터 유형을 가질 수 있습니다.
-
T
는 구조체 유형 또는 포인터 유형입니다. -
T
는 이러한 기준을 모두 재귀 적으로 충족시키는 구성원 만 포함합니다.
배열 액세스를위한 포인터
이 예제는 C # 배열에 대한 C와 같은 액세스를 위해 포인터를 사용하는 방법을 보여줍니다.
unsafe
{
var buffer = new int[1024];
fixed (int* p = &buffer[0])
{
for (var i = 0; i < buffer.Length; i++)
{
*(p + i) = i;
}
}
}
포인터 액세스는 일반적으로 C # 배열에 액세스 할 때 일반적으로 발생하는 범위 검사를 내 보내지 않으므로 unsafe
키워드가 필요합니다.
fixed
키워드는 C # 컴파일러에게 예외적 인 방식으로 객체를 고정하는 명령을 내 보내야한다고 알려줍니다. 가비지 컬렉터가 배열을 메모리에서 이동하지 않도록 보장하려면 배열에서 가리키는 포인터를 무효화해야하므로 고정해야합니다.
포인터 연산
포인터의 더하기 및 빼기는 정수와 다르게 작동합니다. 포인터가 증가하거나 감소 할 때 포인터가 가리키는 주소는 지시 대상 유형의 크기만큼 증가되거나 감소됩니다.
예를 들어, int
( System.Int32
별칭) 형식의 크기는 4입니다. int
를 주소 0에 저장할 수 있으면 후속 int
를 주소 4에 저장할 수 있습니다. 코드에서 :
var ptr = (int*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 4
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8
마찬가지로 long
( System.Int64
별칭) 형식의 크기는 8입니다. long
을 주소 0에 저장할 수 있으면 후속 long
을 주소 8에 저장할 수 있습니다. 코드에서 :
var ptr = (long*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr)); // prints 0
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 8
ptr++;
Console.WriteLine(new IntPtr(ptr)); // prints 16
void
유형은 특별하며 void
포인터도 특수하며 유형을 알 수 없거나 중요하지 않을 때 catch-all 포인터로 사용됩니다. Size-Agnostic 특성으로 인해 void
포인터는 증가 또는 감소 할 수 없습니다.
var ptr = (void*)IntPtr.Zero;
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));
ptr++; // compile-time error
Console.WriteLine(new IntPtr(ptr));
별표는 유형의 일부입니다.
C 및 C ++에서 포인터 변수 선언의 별표 (*)는 선언 되는 표현식의 일부입니다 . C #에서는 선언의 별표 가 해당 유형의 일부입니다 .
C, C ++ 및 C #에서 다음 코드 조각은 int
포인터를 선언합니다.
int* a;
C 및 C ++에서 다음 스 니펫은 int
포인터와 int
변수를 선언합니다. C #에서는 두 개의 int
포인터를 선언합니다.
int* a, b;
C와 C ++에서 다음 스 니펫은 두 개의 int
포인터를 선언합니다. C #에서는 유효하지 않습니다.
int *a, *b;
빈*
C #은 C 및 C ++에서 void*
유형에 상관없이 사용하고 크기를 인식하지 않는 포인터로 상속합니다.
void* ptr;
암시 적 변환을 사용하여 모든 포인터 유형을 void*
할당 할 수 있습니다.
int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
반대의 경우에는 명시적인 변환이 필요합니다.
int* p1 = (int*)IntPtr.Zero;
void* ptr = p1;
int* p2 = (int*)ptr;
회원 액세스 ->
C #은 C 및 C ++에서 기호 사용을 상속합니다. ->
형식화 된 포인터를 통해 인스턴스 멤버에 액세스하는 수단으로 사용됩니다.
다음 구조체를 고려하십시오.
struct Vector2
{
public int X;
public int Y;
}
다음은 회원에 액세스하기 위해 ->
사용의 예입니다.
Vector2 v;
v.X = 5;
v.Y = 10;
Vector2* ptr = &v;
int x = ptr->X;
int y = ptr->Y;
string s = ptr->ToString();
Console.WriteLine(x); // prints 5
Console.WriteLine(y); // prints 10
Console.WriteLine(s); // prints Vector2
일반 포인터
포인터를 지원하기 위해 타입이 만족해야만하는 기준 ( 비고 참조)은 일반적인 제약 조건으로 표현 될 수 없다. 따라서 제네릭 형식 매개 변수를 통해 제공된 형식에 대한 포인터를 선언하려는 시도는 실패합니다.
void P<T>(T obj)
where T : struct
{
T* ptr = &obj; // compile-time error
}