TECH 으로 돌아가기
TECH HACKER NEWS 오늘 10분 읽기 29 READS

C 언어는 왜 2026년에도 '미정의 동작'과 씨름하는가

C 언어는 왜 2026년에도 '미정의 동작'과 씨름하는가
SOURCE IMAGE · HACKER NEWS

생체의학공학 교수인 마르틴 우에커(Martin Uecker)는 전형적인 컴파일러 전문가의 이력과는 거리가 있지만, C 표준화 작업에 깊이 관여해 온 인물이다. 그가 던진 질문은 단순하다. 2026년에도 왜 굳이 C인가. 그의 답은 명확했다. C는 여전히 훌륭한 언어라는 것이다. 이식성이 높고 오랜 기간 안정적이며, 컴파일이 빠르고 생성된 기계어도 빠르다. 코드를 보면 컴퓨터가 실제로 무엇을 할지 대체로 짐작할 수 있는 '보이는 그대로(what you see is what you get)'의 성격을 가졌고, 도구 생태계가 풍부하며 필요할 때는 언어가 개발자를 방해하지 않고 비켜선다. 수십 년간 인프라의 바닥을 지탱해 온 언어가 여전히 현역인 데는 이유가 있다는 것이다.

문제는 이 언어의 긴 역사가 오늘날까지 그림자를 드리운다는 점이다. 1989년 C89 표준은 부호-크기 표현이나 1의 보수 표현을 쓰는 정수, 세그먼트 메모리, 기이한 포인터 표현, 예상 밖의 타입 크기를 가진 다양한 하드웨어를 모두 아울러야 했다. 일부 허니웰 기계는 9비트 바이트를 썼다. 이런 환경에서 이식 가능한 코드를 작성할 수 있는 표준을 만들려다 보니, 표준은 언어의 의미를 실제 하드웨어가 아닌 '추상 기계(abstract machine)'로 규정하는 길을 택했다. 모든 연산은 그 추상 기계 위에서 실행된 것처럼 동작해야 하고, 프로그램의 관찰 가능한 동작(observable behavior)만 추상 기계와 일치하면 된다. volatile 변수 접근처럼 관찰 가능하다고 정의된 것은 정확히 따라야 하지만, 나머지는 결과만 같으면 컴파일러가 자유롭게 구현할 수 있다.

자유의 대가, 미정의 동작

이 자유의 반대편에 '미정의 동작(undefined behavior)'이 있다. 프로그램이 이식 불가능하거나 표준이 전혀 정의하지 않은 일을 할 때, C89는 구현에 '아무런 요구도 부과하지 않는다'고 선언한다. 미정의 동작은 나름의 쓸모가 있다. 확장 기능을 지원하고, 하드웨어 기반 안전 장치와의 상호작용을 다루며, 공격적인 최적화를 가능하게 하고, 탐지하기 어려운 오류를 무시할 권리를 컴파일러에 준다. 하지만 우에커가 지적한 핵심 문제는, 표준이 미정의 동작에 대해 컴파일러가 말 그대로 무엇이든 해도 된다고 허용한다는 데 있다. 프로그램에 미정의 동작이 조금이라도 있으면 컴파일러 제작자들의 관점에서 그 프로그램은 아무런 기대 가능한 의미를 갖지 않는다. C++23은 한 걸음 더 나아가 이런 프로그램에 어떤 요구도 부과하지 않는다고 명시했다. 그 결과 '언어로부터 무엇을 기대할 수 있는가'를 두고 개발자들 사이에 광범위한 이견이 생겼다.

구체적인 사례가 그 간극을 드러낸다. memset()으로 구조체 전체를 0으로 채운 뒤 특정 필드만 쓰고 나서 패딩 바이트를 읽으면 어떻게 될까. 거기에 보안상 민감한 데이터가 남을 수 있을까. 2015년의 한 조사에서도 이에 대한 합의는 없었다. 또 다른 예로, 어떤 값으로 나누는 연산에서 분모가 0이면 그것은 0으로 나누기, 즉 미정의 동작이다. 이때 컴파일러가 분모 검사를 아예 빼고 곧장 x에 42를 대입해도 되는가. 그런 컴파일러는 실제로 존재한다. 그러나 비슷해 보이는 다른 코드, 즉 그 결과를 함수 g()에 넘기는 경우에 검사를 생략해 버리면 이는 명백한 버그다. g()가 분모가 0일 때 exit()를 호출하도록 정의돼 있다면 나눗셈은 영영 실행되지 않고, 따라서 프로그램의 동작은 미정의가 아니기 때문이다.

'시간 여행'을 막고, 악마를 몰아내다

더 미묘한 쟁점은 컴파일러가 마지막 나눗셈 연산을 x에 대한 대입 위로 끌어올려도(hoist) 되는가다. 0으로 나누는 경우에 아무 의미가 없다면 관찰 가능한 동작에는 변화가 없다는 논리인데, 이런 '시간 여행'식 최적화도 실제로 행해졌다. C23은 이를 금지하는 '노 타임 트래블(no time travel)' 조항을 추가했고, C++은 대신 std::observable_checkpoint() 호출을 삽입해 명시적으로 막도록 했다. 이런 시간 여행 버그는 점차 사라지겠지만, 표준이 분명한데도 컴파일러 제작자들의 해석이 엇갈리는 영역은 여전히 많다. 거의 항상 정의돼 있는 초기화되지 않은 변수의 읽기, 언제나 정의돼 있지만 Clang과 GCC 양쪽에서 잘못 컴파일되는 포인터 동등 비교 등이 그렇다.

C 위원회는 이 문제들을 다루기 위해 메모리 객체 모델, 메모리 안전성, 미정의 동작을 각각 전담하는 세 개의 연구 그룹을 운영하고 있다. 현재 표준에는 약 100건의 미정의 동작이 있는데, 작업 중인 C2y 초안은 그중 45건을 제거했다. 상황은 실제로 나아지고 있다는 것이다. 이를 찾아내는 도구도 풍부해지고 있다. 컴파일러 경고, 정적 분석기, 새니타이저, LLM 기반 도구, 형식 검증 등이 있으며, 정수 오버플로나 사용 후 해제 가능성에 대한 경고가 개선됐다. 정적 분석기는 점점 컴파일러 자체에 내장되고 있어, GCC는 다양한 버퍼 오버플로 가능성을 경고할 수 있다. 새니타이저는 런타임 검사를 삽입해 많은 미정의 동작을 잡아내며, 트래핑 모드에서는 보안 강화 수단으로도 쓸 수 있다.

메모리 안전성은 C의 강점이었던 적이 없지만, 우에커는 개선이 가능하다는 점을 강조했다. 문제는 타입 안전성, 공간적 메모리 안전성, 시간적 메모리 안전성으로 나뉜다. C의 타입 체계는 강력한 편이어서, 태그 없는 공용체가 일으키는 타입 혼동은 추가 주석으로 컴파일러가 강제할 수 있고, void로부터의 위험한 캐스트도 새 진단으로 잡을 수 있으며, 번역 단위 간 타입 검사는 링크 시점 검사기로 보완할 여지가 있다. 공간적 안전성, 즉 경계 검사는 부분적으로 해결됐으며 counted_by 같은 속성을 쓰면 가변 배열 멤버에 대한 검사가 가능해진다. 반면 사용 후 해제를 막는 시간적 안전성은 더 어렵고, 이 지점에서는 Rust가 분명한 우위에 있다. 그럼에도 CHERI 같은 아키텍처나 Fil-C 같은 도구가 상당수의 시간적 안전성 버그를 잡아낼 수 있다.

실무자가 읽어야 할 지점

그렇다면 이 도구와 언어 변화를 모두 동원하면 완전한 메모리 안전성에 도달할 수 있을까. 우에커는 문제를 완전히 풀려면 비싼 런타임 검사이거나 형식 검증이 필요하며, 가까운 미래에 가장 완성도 높은 결과는 제한된 언어와 형식 검증 도구의 조합에서 나올 것이라고 봤다. 실무자 입장에서 이 논의의 의미는 분명하다. C의 미정의 동작은 '컴파일러 버그'로 치부할 수 있는 변덕이 아니라 표준이 부여한 자유의 구조적 결과이며, 같은 코드가 컴파일러와 버전에 따라 다르게 동작할 수 있다는 전제를 깔고 코드를 작성해야 한다는 것이다. 바꿔 말해 경고를 최대한 켜고 정적 분석기와 새니타이저를 파이프라인에 넣는 것은 선택이 아니라 기본기에 가깝다. 동시에 기대할 수 있는 것에도 한계는 있다. C만으로 Rust 수준의 시간적 안전성을 보장하기는 어렵고, '완전한 메모리 안전성'은 여전히 가능성의 영역에 머문다.

C23은 K&R 방식의 구식 함수 정의, 부호-크기 및 1의 보수 기계 지원, 트라이그래프 같은 문제적 기능을 들어냈고 비트 정밀 정수 타입과 검사된 정수 연산 등을 더했다. C2y는 case 범위, 이름 붙인 for 루프, 배열 길이를 구하는 _Countof() 매크로, 그리고 다량의 '악마 제거'를 예고하고 있다. 완전한 메모리 안전성에는 이르지 못하겠지만 시간이 갈수록 더 현실적인 목표가 될 것이라는 전망이다. 우에커는 관심 있는 이들에게 워킹 그룹 참여를 권하며 발표를 마쳤다. 수십 년 된 언어가 조용히, 그러나 꾸준히 자신의 가장 날카로운 모서리를 갈아내고 있다는 사실 자체가 이 논의의 가장 실질적인 결론일지도 모른다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://lwn.net/Articles/1095811/
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기 →
처리 중...