TIL

[TIL] C#은 주소(메모리) 접근을 어떻게 할까?

DearGreen 2024. 9. 30. 20:45

C#은 주소 접근을 어떻게 할까?


C / C++ 등을 ' * ' 참조 연산자 혹은 ' & ' 주소 연산자등을 통해 주소에 접근하곤 한다. 그렇다면, C#에서는 주소 접근을 어떻게 할까?

작성하는 C# 코드 대부분은 “확인할 수 있는 안전한 코드”입니다. 확인할 수 있는 안전한 코드란 .NET 도구에서 코드가 안전한지 확인할 수 있음을 의미합니다. 일반적으로 안전한 코드는 포인터를 사용하여 메모리에 직접 액세스하지 않습니다. 또한 원시 메모리를 할당하지 않습니다. 대신 관리형 개체를 만듭니다.

C#은 unsafe 컨텍스트를 지원하는데, 이 컨텍스트에서는 확인할 수없는 코드를 작성할 수 있습니다. unsafe 컨텍스트에서 코드는 포인터를 사용하고, 메모리 블록을 할당 및 해제하고, 함수 포인터를 사용하여 메서드를 호출할 수 있습니다. C#의 안전하지 않은 코드가 반드시 위험한 것은 아닙니다. 단지 CLR에서 안전을 확인할 수 없을 뿐입니다.

출처 : https://learn.microsoft.com/ko-kr/dotnet/csharp/language-reference/unsafe-code

 

마이크로소프트 공식 홈페이지에서 '안전한 코드는 포인터를 사용해 메모리에 직접 액세스하지 않습니다.'  라고 표현할 정도로 사용할 수는 있지만 포인터 변수등을 통한 직접적인 접근은 부적절하다고 나와 있다. 그렇다면, 어떻게 힙 영역에 있는 변수에 접근하고 활용하는가?

 

C#의 주요 목표는 메모리 관리를 자동화해서 안전성을 높이는 것이다. 포인터 대신 참조 타입과 가비지 컬렉션을 사용한다. 거의 웬만한 자료등은 직접 메모리에 접근하지 않아도 그 값을 알 수 있도록 C#에서 함수를 제공해 준다는 말이다.

 

아래 실제 간편한 예시를 보자.

  • 클래스와 객체: 클래스는 힙에 할당되며, 인스턴스를 생성하고 이를 통해 힙에 있는 데이터에 접근할 수 있다.
class MyClass
{
    public int Value { get; set; }
}

MyClass myObject = new MyClass();
myObject.Value = 10; // 힙에 있는 변수에 접근

 

  • 컬렉션: List<T>, Dictionary<K, V>와 같은 컬렉션 클래스를 사용하여 동적으로 메모리를 관리할 수 있다.
using System.Collections.Generic;

List<int> numberList = new List<int>();
numberList.Add(1); // 힙에 있는 리스트에 접근

 

  • 배열: 배열도 참조 타입이며, 배열을 다른 변수에 할당하면 원본 배열을 참조한다.
int[] array1 = { 1, 2, 3 };
int[] array2 = array1; // array2는 array1을 참조
array2[0] = 10;
Console.WriteLine(array1[0]); // 10이 출력됨

 

  • ref & out 키워드 : out 키워드는 변수를 참조로 전달한다. 또한 in 키워드는 변수를 읽기 전용 참조로 전달한다.
void GetValues(out int value)
{
    value = 42; // 반드시 값을 설정해야 함
}

GetValues(out int result);
Console.WriteLine(result); // 42가 출력됨

void PrintValue(in int number)
{
    Console.WriteLine(number);
}

int myValue = 10;
PrintValue(in myValue); // 10이 출력됨