5. Span<T>/stackalloc 와 ref struct 의 스택 전용 제약
난이도 상 해설 보기 →
결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커
(A)(B) 는 주목 위치 힌트다.
결함 코드 · C#
// ============================================================================
// [언어 내부동작 문제] C# - Span<T>/stackalloc 와 ref struct 의 스택 전용 제약
// ----------------------------------------------------------------------------
// 주제: Span<T> 가 왜 ref struct(스택 전용)인지, stackalloc 과 결합한 무할당(zero-alloc)
// 파싱, 그리고 ref struct 가 힙/비동기/클로저로 "탈출(escape)" 하지 못하는 이유.
//
// 아래는 패킷 헤더를 힙 할당 없이 읽는 파서다. 이어서 ref struct 제약을 시험하는 여러
// 코드 조각이 주석으로 제시된다.
//
// 과제(코드를 고치는 문제가 아니라, 컴파일 가부와 내부 동작을 예측·설명하는 문제):
// 1) ParseHeader 가 왜 힙 할당 없이 동작하는지(Span/stackalloc/슬라이싱) 설명하라.
// 2) 아래 (F1)~(F5) 각각이 컴파일되는지/안 되는지 예측하고, 그 이유를 ref struct 의
// 수명·탈출 규칙으로 설명하라.
// 3) Span 이 ref struct 가 아니었다면(일반 struct/class였다면) 어떤 안전성 문제가 생겼을지,
// 그리고 비동기 경로에서 무할당이 필요할 때 무엇을 쓰는지(Memory<T>) 설명하라.
// (먼저 직접 예측을 적은 뒤 answer.md 와 대조할 것)
// ============================================================================
using System;
using System.Buffers.Binary;
using System.Threading.Tasks;
public readonly ref struct Header // ref struct: 스택 전용
{
public readonly ushort Type;
public readonly ushort Len;
public Header(ushort type, ushort len) { Type = type; Len = len; }
}
public class Parser
{
// 힙 할당 없이 헤더 + payload 슬라이스를 얻는다
public Header ParseHeader(ReadOnlySpan<byte> buf, out ReadOnlySpan<byte> payload)
{
ushort type = BinaryPrimitives.ReadUInt16LittleEndian(buf);
ushort len = BinaryPrimitives.ReadUInt16LittleEndian(buf.Slice(2));
payload = buf.Slice(4, len); // 복사 없이 같은 메모리의 뷰
return new Header(type, len);
}
public void Demo()
{
Span<byte> tmp = stackalloc byte[8]; // 스택 메모리, GC 무관
tmp[0] = 1;
// ... tmp 를 채워 ParseHeader 에 넘길 수 있음
}
// ── 아래 조각들이 컴파일되는지 예측하라(이유 포함) ──────────────────────────
// (F1) object o = (object)someSpan; // Span 을 object 로 박싱
// (F2) class Box { Span<byte> field; } // Span 을 클래스 필드로
// (F3) Func<int> f = () => someSpan[0] + 1; // Span 을 람다로 캡처
// (F4) async Task A() { Span<byte> s = stackalloc byte[4]; await Task.Yield(); s[0]=1; }
// (F5) Span<byte> s = stackalloc byte[4]; foreach (var x in list) { ... } // await 없는 동기 사용
} 내 리뷰 · C#
내 답안 · 자동 저장
작성 후 위 해설 보기에서 모범 해설과 대조하세요.