ONE QUESTION AT A TIME.

천천히 생각하고, 분명하게.

TODAY’S QUESTION318 / 1500

문자열 일부의 string_view.data()를 C 문자열 함수에 넘기면 어떤 경계를 잃나요?

잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?

답변과 점검 포인트 펼치기답변 접기
01

구두 답변

정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.

string_view의 논리 범위는 (data, size)지만 data()만 C API에 넘기면 size 경계를 잃습니다. "prefix|payload|tail"에서 offset 7, length 7인 view는 payload를 뜻하지만, %s나 strlen은 그 길이를 모르므로 뒤의 |tail까지 읽고 원본의 null에서 멈출 수 있습니다. 접근 가능한 null이 없다면 종료를 찾으려고 view 밖을 읽어 undefined behavior가 됩니다. 이것은 “조금 긴 출력”일 수도 있는 문제가 아니라, 원본 저장소 상태에 따라 UB가 되는 계약 위반입니다. 길이 기반 API에는 두 인자를 함께 전달하고, null 종료만 받는 API에는 std::string owned(part); owned.c_str()로 경계를 복사합니다.

예를 들어 std::string source="prefix|payload|tail"; std::string_view part(source.data()+7,7);에서 part.size()는 7이지만 part.data()부터 null까지는 payload와 tail을 포함합니다. 이 예에서는 source가 살아 있고 전체 string이 null 종료되어 있어 과도한 출력이 발생하는 경로를 설명할 수 있습니다. 그러나 길이 7의 배열에 null을 넣지 않은 경우라면 strlen의 반환값을 예상 숫자로 쓰면 안 됩니다. 정의되지 않은 읽기를 실행하여 보여 주는 대신 길이 계약 위반으로 진단해야 합니다.

write(fd, part.data(), part.size())처럼 길이를 받는 API도 partial write·오류 처리와 원본 수명은 따로 지켜야 합니다. printf("%.*s", length, part.data())는 길이 제한을 줄 수 있지만 int 범위 변환과 내장 null에서의 중단 의미가 있어 임의 바이너리 전송과 같지 않습니다. C API가 포인터를 호출 뒤까지 보관한다면 지역 owned.c_str()도 충분하지 않습니다. 데이터를 어디까지 읽고 언제까지 보관하는지 두 축을 확인한 뒤, 필요한 범위와 수명을 가진 소유 객체를 선택하겠습니다.

02

득점 포인트

이해가 드러나는 지점을 확인해 보세요.

  • data와 size를 함께 전달해야 view 경계가 보존된다는 점을 수치 예제로 설명합니다.
  • 원본 뒤에 null이 우연히 있는 overread와 null이 없어지는 UB를 구분합니다.
  • 길이 기반 API와 C 문자열 API의 안전한 변환을 나눕니다.
03

감점 포인트

놓치기 쉬운 조건과 오해를 짚어 보세요.

  • 모든 data()가 자동으로 null-terminated라고 말합니다.
  • C 함수가 string_view의 size를 알아서 읽는다고 가정합니다.
  • 한 실행에서 원하는 출력이 나왔으므로 UB가 안전하다고 결론냅니다.
04

더 파고들 거리

하나 더 질문하며 이해의 범위를 넓혀 보세요.

  • span<const char>를 C API에 전달할 때 string_view와 달리 확인해야 하는 계약은 무엇인가요?
  • 복사 없이 null 종료를 만족시킬 수 있는 원본 조건과 불가능한 조건을 어떻게 판별하나요?

득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.

← 전체 질문 보기