2 분 소요

회사에서 게임관리툴을 만들기 위해 C#을 애용하고 있다. 이유는 무엇보다도 생산성이 높으니까. MFC 같은 것으로 해도 되지만 MFC는 번거로운 면도 많을뿐더러 MS가 C#을 밀고 있기 때문이다.

그런데 C#을 하다보면 C++에서 나왔던 습관들이나 C++에서 쓰던 것들이 안 보여서 난처할 때가 가끔 있다. 예를 들면, memcpy()typedef 같은 것들 말이다.

2026년 수정 안내 원래 이 글에는 “C#에는 memcpy()나 typedef 같은 것들이 없다”고 적었는데, 정확하지 않았다. 둘 다 대응하는 기능이 C#에 있다. 이름과 형태가 다를 뿐이다. 바로잡고, C#이 이후 크게 바뀐 부분도 덧붙인다.

memcpy에 대응하는 것

용도에 따라 다르다.

배열을 그대로 복사할 때 — Array.Copy / Buffer.BlockCopy

int[] src = { 1, 2, 3, 4, 5 };
int[] dst = new int[5];

Array.Copy( src, dst, src.Length );          // 타입을 아는 배열 복사
Buffer.BlockCopy( src, 0, dst, 0, 20 );      // 바이트 단위 복사. memcpy에 가장 가깝다

포인터로 메모리를 직접 다뤄야 할 때 — Marshal.Copy / unsafe + Buffer.MemoryCopy

네이티브 코드와 데이터를 주고받거나 포인터 연산이 필요할 때는 unsafe 컨텍스트에서 진짜 C++ 스타일로 쓸 수도 있다. fixed로 고정하는 포인터의 타입은 배열의 원소 타입과 일치해야 한다 — int[]를 곧바로 byte*로 고정할 수는 없다.

int[] srcInt = { 1, 2, 3, 4, 5 };
int[] dstInt = new int[5];

unsafe
{
    fixed ( int* pSrc = srcInt, pDst = dstInt )   // 배열 원소 타입(int)과 맞춘다
    {
        // Buffer.MemoryCopy는 void*를 받으므로 byte*로 바꿀 필요조차 없다.
        Buffer.MemoryCopy( pSrc, pDst, dstInt.Length * sizeof( int ), srcInt.Length * sizeof( int ) );
    }
}

네이티브 버퍼(IntPtr)와 관리되는 배열 사이라면 Marshal.Copy를 쓴다.

Marshal.Copy( nativeIntPtr, managedByteArray, 0, length );

typedef에 대응하는 것

타입 별칭 — using

using PlayerId = System.Int32;
using PlayerTable = System.Collections.Generic.Dictionary<int, PlayerInfo>;

C++의 typedef가 하던 일과 거의 동일하다. using 별칭은 지금도 파일 맨 위에 두는 지시문이라는 점은 그대로다. C# 12에서 바뀐 것은 위치가 아니라 별칭으로 지정할 수 있는 타입의 범위다 — 이전에는 이름 있는 타입만 별칭으로 지정할 수 있었지만, C# 12부터는 튜플·배열·포인터 같은 타입도 별칭으로 지정할 수 있게 되었다.

using Point = (int x, int y);   // C# 12부터 가능 - 튜플 타입도 별칭 지정

함수 포인터에 대응하는 것 — delegate

C++에서 함수 포인터 타입을 typedef로 이름 붙이던 용도라면, C#에서는 delegate가 그 역할이다.

public delegate void PacketHandler( byte[] data );

PacketHandler handler = OnReceivePacket;

C#은 그 사이 많이 바뀌었다

원래 이 글에서 소개했던 참고 문서는 2007년, Visual Studio 2008 기준이었다. “달라진 게 없을 것”이라고 적었는데, 실제로는 그 뒤로 C#이 크게 바뀌었다.

  • LINQ (C# 3.0, 2007) — 컬렉션을 질의문처럼 다룬다.
  • async/await (C# 5.0, 2012) — 비동기 코드를 동기 코드처럼 쓸 수 있게 됐다.
  • dynamic (C# 4.0, 2010), 패턴 매칭 (C# 7~9), nullable 참조 타입 (C# 8.0), 레코드(C# 9.0) 등.

지금 C++ 개발자가 C#으로 넘어온다면, 당시 문서보다는 Microsoft Learn의 C# 문서를 참고하는 편이 훨씬 정확하고 최신이다. 원래 링크했던 MSDN 문서는 현재 접속되지 않는다.

참고자료

댓글 남기기