널(null) 포인터 역참조는 런타임에서 잡기 가장 쉬운 종류의 잘못된 메모리 주소이며, 메모리 안전하지 않은 언어에서 발생하는 잘못된 주소 중에서도 오히려 드문 편이다. 널을 없애는 단순한 해법에는 눈에 잘 띄지 않는 여러 트레이드오프가 있고, 널이 ‘문제’라고 여겨지는 이유는 특정한 ‘개별 요소’ 중심 사고방식에서 비롯된다.
URL: https://www.gingerbill.org/article/2026/01/02/was-it-really-a-billion-dollar-mistake/
2026-01-02
TL;DR null 포인터 역참조는 경험적으로 런타임에서 잡기 가장 쉬운 종류의 잘못된 메모리 주소이며, 메모리 안전하지 않은 언어에서 발생하는 잘못된 메모리 주소 중에서도 가장 흔하지 않은 종류다. “문제”인 null 포인터를 없애는 사소한 해법들은 눈에 잘 보이지 않는 수많은 트레이드오프를 동반하며, 사람들이 그것을 “문제”라고 생각하게 되는 원인은 특정한 종류의 ‘개별 요소’ 중심 사고방식에서 나온다.
이 글의 2부에서 더 많은 내용을 명확히 다룬다: 10억 달러짜리 실수를 완화하기
아마 대부분의 사람들은 _10억 달러짜리 실수(Billion Dollar Mistake)_라는 말을 들어봤을 텐데, 이는 null 참조/포인터의 발명자인 토니 호어(Tony Hoare)가 만들었다.
나는 그것을 내 10억 달러짜리 실수라고 부른다. 그것은 1965년에 널 참조(null reference)를 발명한 일이었다. 그 당시 나는 객체 지향 언어(ALGOL W)에서 참조를 위한 최초의 포괄적인 타입 시스템을 설계하고 있었다. 내 목표는 참조의 모든 사용이 절대적으로 안전하도록, 컴파일러가 자동으로 검사를 수행하게 만드는 것이었다. 하지만 구현이 너무 쉬웠기 때문에 널 참조를 넣고 싶은 유혹을 떨칠 수 없었다. 이로 인해 수없이 많은 오류, 취약점, 시스템 크래시가 발생했고, 지난 40년 동안 아마도 10억 달러에 달하는 고통과 피해를 야기했을 것이다.
— Tony Hoare, 2009
내가 한 가지 언급하고 싶은 점은, 40년에 걸쳐 업계 전체에서 10억 달러는 문자 그대로 아무것도 아니라는 점이며, 반올림 오차에 가깝다는 것이다. 나는 그 숫자가 실제 추정치가 아니라 과장법(hyperbole)이라고 가정한다. 오늘날에는 이보다 훨씬 더 비싼 실수를 저지르는 소프트웨어 산업이 수두룩하기 때문이다. 그럼에도 나는 이 “문제”에 대해 계속 이야기해보고 싶다.
메모리를 다루는 거의 모든 프로그래밍 언어를 사용해봤다면 null 포인터를 마주친 적이 있을 것이다. nullptr, NULL, nil 또는 유사한 다른 많은 이름으로 불리기도 한다. 보통은 유효한 객체를 가리키지 않는 포인터/참조로 사용된다. 하지만 null을 다른 방식으로 생각하면, 수많은 잘못된 메모리 주소(invalid memory address) 중 하나일 뿐이다.
메모리를 관리(garbage collect)하는 많은 언어에서는 다른 잘못된 메모리 상태에 도달하기가 매우 어렵기 때문에, null이 가장 흔한 잘못된 메모리 주소가 될 것이다. 하지만 null에 대한 일반적인 비판은 C나 Odin 같은 시스템 레벨 언어에서 더 자주 등장하며, 나는 실제로는 그런 언어들에서 대부분의 잘못된 메모리 주소가 null이 아니라고 주장할 것이다.
null 포인터 문제는 술 취한 사람의 탐색 원리(가로등 아래가 밝으니 그 아래서만 잃어버린 열쇠를 찾는 것)와 관련이 있다. 내 경험상 null 포인터는 보통 찾고 고치기 매우 쉽다. 특히 대부분은 사소한 버그(대개 오타)로 인해 생기기 때문이다.
이론적으로 null은 여전히 완전히 유효한 메모리 주소일 수 있지만, практи적으로 우리는 null이 0이라는 관례가 “포인터가 설정되지 않았음(unset)”을 표시하는 데 유용하다고 결정해 왔다. 현대 플랫폼은 이런 오류를 잡기 위해 (가상) 메모리의 첫 몇 페이지를 예약해 둔다. 일반적으로 모든 현대 시스템에서 그 메모리 주소는 0에 위치한다1. 하지만 모든 플랫폼에서 항상 그랬던 것은 아니다2. 즉 null은 메모리 주소를 위한 일종의 센티널(sentinel) 값일 뿐이다.
하지만 여기서 진짜 질문은 이것이다: null이 일반적으로 실수로 여겨진다면, 그 실수의 원인은 무엇이며 정말로 문제인가?
실제로 null 포인터의 문제는 null 포인터 자체의 존재가 아니라, 그것을 역참조(dereference)했을 때 런타임 패닉이 발생한다는 점이다. 앞서 말했듯 null 포인터는 잘못된 메모리 주소의 한 종류일 뿐이며, C나 Odin 같은 언어에서는 null이 아닌 다른 종류의 잘못된 메모리 주소를 얻게 되는 일이 더 흔하다. 그리고 null 포인터 역참조라는 증상을 제거한다고 해서 이런 문제들이 해결되지는 않는다. 다른 흔한 잘못된 메모리 주소의 형태로는 use-after-free, 잘못된 포인터 산술(pointer arithmetic) 계산, 매핑되지 않은(unmapped) 메모리 영역 등등이 있으며, 이는 null 포인터처럼 사소하게 해결할 수 있는 것들이 아니다.
컴파일 타임 메모리 안전성은 내가 하는 시스템 레벨 프로그래밍에서는 필수라고 생각하지 않지만, Rust나 Ada 같은 언어는 그런 것이 필요할 때 보통 선택되는 언어다.
Odin에서는 nil 포인터의 존재를 제거하지 않기로 결정했다. 이 “문제”를 “해결”하려 하지 않은 이유는, 프로그래밍 언어 설계에서 즉시 드러나지 않는 트레이드오프이기 때문이다.
기본적으로 포인터가 nil 상태를 갖지 않게 만들려면 두 가지 중 하나가 필요하다: 프로그래머가 포인터를 사용할 때마다 테스트하도록 강제하거나, 포인터가 nil일 수 없다고 가정하는 것이다. 전자는 정말 성가시고, 후자는 내가 하고 싶지 않았던 무언가를 요구한다(시작부터 나쁜 것처럼 보이지 않기 때문에 아마 동의하지 않을 것이다): 모든 값의 모든 곳에서의 명시적 초기화.
근본적으로 nil 포인터/참조 문제가 발생하는 이유는, 생성 시 개별 요소(individual-element)를 명시적으로 초기화하지 않아도 되며 그 결과 기본적으로 “nil” 상태가 될 수 있기 때문이다.
첫 번째 옵션은 많은 언어(부분적으로는 Odin 포함)에서 maybe/옵션 타입 또는 널러블(nullable) 타입(모나드)을 지원함으로써 해결된다. 그러나 나는 실전에서 이것들을 그다지 좋아하지 않는다. 시스템 레벨 프로그래밍에서는 드물게 필요하기 때문이다. 나는 이것이 _“논쟁적”_인 의견이라는 걸 잘 안다. 하지만 시스템 레벨 언어는 항상 메모리를 다루며, 의도적으로 “unsafe” 상태에 쉽게 들어갈 수 있다. 이를 제한하면 (커스텀 할당자 같은) 구성 요소의 구현이 매우 어려워질 수 있고, 그 외에도 다양한 것들이 힘들어진다.
Odin에는 Maybe(^T)가 있으며, 존재하는 건 괜찮다. 하지만 실제로 필요한 경우는 드물다. 필요할 때는 대개 외부 코드의 포인터 사용(즉 비 Odin 코드)을 문서화하기 위해서이거나, (Odin 코드에서는) 포인터가 아닌 것들을 위해서다. 그리고 Odin의 다른 기능들(다중 반환값, or_return 등, 적절한 배열 타입 등)과 결합되면, 대부분의 경우 필요성이 크게 줄어든다.
참고: ML/Rust 사용자에게, Odin의 Maybe(^T)는 의미론과 최적화 측면에서 Rust의 Option<&T>와 동일하다.
두 번째 옵션은 미묘하다. 프로그래밍할 때 특정 스타일과 아키텍처 관행을 강제하기 때문이다. Odin은 두 가지를 중심으로 설계됐다: 프로그래밍할 때 C처럼 느껴지는 C 대안이 되는 것, 그리고 “제로 값(zero value)을 유용하게 만들기”다. 따라서 언어와 코어 라이브러리의 많은 구성 요소가 이 목표를 중심으로 짜여 있다. Odin은 C 대안을 지향하므로, 대다수 C 프로그래머가 실제로 프로그래밍하는 방식을 애초에 바꾸려 하지 않는다. 그래서 명시적 초기화 없이 변수를 선언할 수 있게 허용한다. 다만 C와의 차이는, 변수들이 기본적으로 0으로 초기화된다는 점이다3.
근본적으로 이런 _모든 곳에서의 명시적 개별 요소 기반 초기화_라는 발상은 일종의 전염성(viral) 개념이며, 장기적으로는 나쁜 아키텍처 결정을 낳는다고 생각한다. 컴파일러는 멍청하다. 특히 코드 전반에 걸친 아키텍처 결정의 비용을 아는 것은 컴파일러가 해줄 수 있는 일이 아니다. 그건 코드의 (전역적인) 의도를 알아야 하기 때문이다. 많은 명시적 초기화가 “최적화로 제거될 수 있다”고 주장하는 사람들은 개별 요소라는 로컬 관점에서만 생각하는 것이다. 하지만 더 _글로벌_한 스케일에서는 어떤 경우 오히려 느려지는 비용으로 누적된다.
예를 들어 make([]Some_Struct, N)을 보자. Odin에서는 메모리를 그냥 0으로 초기화한다. 어떤 경우에는 그게 정말 공짜이기 때문이다(예: mmap은 반드시 0으로 초기화해야 한다). 하지만 슬라이스의 각 값을 초기화해야 한다면, 이제 O(1) 문제를 O(N) 문제로 바꾸는 셈이다. 그리고 구조체의 각 필드도 각자 생성(construction)이 필요하다면 더 나빠질 수 있다.
나는 nil 포인터 문제가 실증적으로 실전에서 그렇게 큰 문제라고 생각하지 않는다. 많은 사람들이 가능한 한 컴파일 타임에 “증명”하고 싶어 한다는 걸 알지만, 어차피 나는 많은 코드를 테스트해야 한다. 그리고 이 매우 매우 특정한 예제에서는 잡기 매우 사소한(쉬운) 문제다.
그리고 나는 C에서 NULL 포인터 문제가 (전부는 아니지만) 상당 부분은 적절한 배열 타입의 부재 때문에 생긴다고 주장하고 싶다. 내 의견으로 C의 가장 큰 실수는 포인터와 배열을 동일시(conflate)한 것이다. Odin은 경계 검사(bounds-check)되는 서로 다른 배열 타입을 제공하여 이를 해결했다.
새 언어를 비판하는 사람들의 일반적인 습관은, C에 문제가 있으니 새 언어에도 반드시 있을 거라고 말하는 것이다. 그리고 실제로 그런지에 대한 기본적인 조사조차 하지 않는다4.
사람들이 들고오는 이 “함정”5은 아마도 가장 흔한 것 중 하나일 것이다. “명백한” “간단한” 승리처럼 보이기 때문이다. 하지만 나는 그것이 “간단”하지도, 심지어 “승리”도 아니라고 주장한다. 언어 설계는 완벽한 언어가 존재하지 않기 때문에 트레이드오프와 타협의 연속이다. 작업을 위한 도메인 특화 언어(DSL)를 설계하더라도, (단지 문법이 아니라) 구체적인 의미론에서 여전히 수많은 문제가 생긴다.
Rust 같은 언어는 시작부터 _명시적 개별 요소 기반 초기화_를 중심으로 설계되었다. 하지만 Odin을 설계할 때 나는 시작부터 C의 느낌(그리고 다른 설계 철학들)을 유지하고 싶었기 때문에, 암묵적인 0 기반 초기화(예: x: T는 0으로 초기화된 메모리)를 명시적으로 원했다. 이 사소한 선택은 대수롭지 않아 보일 수 있지만, 프로그램을 개발할 때 나중에 거대한 아키텍처 결정들로 이어진다.
Odin은 “그냥” non-nil 포인터를 추가하고 “괜찮아질” 수 없다. 그렇게 하면 더 이상 Odin이 아니게 된다. 언어 전체가 더 이상 C 대안이 아니게 되고, C++, Rust, 또는 OCaml 같은 느낌이 훨씬 강해질 것이다. Rust와 OCaml(Rust가 기반으로 삼은 언어)은 Odin과 다른 종류의 언어이며, 그 접근 방식은 Odin(또는 C)이 하려는 것에 잘 번역되지 않는다.
내가 좋아하는 비유는, 프랑스어가 영어의 약강 5보격(Iambic Pentameter)에 해당하는 것을 못한다고 불평하는 것과 같다. 언어가 다르면 일을 처리하는 접근도 다르다.
나는 이런 로컬 중심 사고가 전역 스코프에서 나쁜 아키텍처 결정을 낳는다고 믿는다. 불행히도 이는 프로그래밍에서 가장 흔한 사고방식 중 하나지만, 모든 프로그래머가 여정에서 거치게 되는 단계이기도 하다. 나와 많은 사람들은 프로그램 아키텍처를 바라보는 사고방식을 진화시켜 왔지만, 대부분의 프로그래머는 여정에서 보통 몇 가지 단계를 거친다.
이 일반적인 사고를 잘 설명한 자료로, 다음 Casey Muratori의 영상이 있다.
이 여정/라이프사이클에서 누구도 프로그래밍을 알며 태어나지 않는다. 배워야 한다. 그리고 프로그래밍에서는(적어도 내 개인적 경험상) 대개 사람들 사이에서 순서가 꽤 일관된 몇 단계로 생각하게 된다. 이 사고 단계의 어느 지점에서 사람들은 내가 _개별 요소 사고방식(Individual-Element Mindset)_이라고 부르는 지점에 도달한다. 이 사고방식은 각각의 데이터 조각(요소)이 고유의 _수명(lifetime)_을 가진다고 생각하는 것이다. 그 결과 각 요소를 개별적으로 생성/malloc하여 고유한 수명을 부여하고, 이후 개별적으로 소멸/free(또는 가비지 컬렉터가 자동으로)하는 사고로 이어진다.
이 사고방식에서 생겨나는 아키텍처는 대개 다음을 중심으로 한다:
new/delete 또는 malloc/freeRAII와 “스마트” 포인터6는 둘 다 이 사고방식의 많은 아키텍처 문제를 우회하기 위한 완화책(mitigation)이다. RAII는 컴파일러가 요소의 생성과 소멸을 자동으로 처리하게 한다. “스마트” 포인터는 할당 해제를 잊는 문제와 use-after-free를 막기 위해, 객체 사용의 수명과 객체 할당의 수명을 결합시키려는 것이다.
이 아키텍처 사고방식은 프로젝트가 커질수록 많은 문제를 만든다. 불행히도 많은 사람들은 프로그래머 여정에서 이 지점을 넘어서지 못한다. 때로는 자동 메모리 관리 언어(예: 가비지 컬렉션 또는 자동 참조 카운팅)만 사용하기 때문에 그럴 수도 있는데, 그런 언어에서는 이런 측면을 거의 생각할 일이 없다. 그리고 설령 생각하게 된다 하더라도, 보통 성능 지향적인 무언가를 하려 할 때 “내가 메모리를 직접 관리하는 척”해야 해서 떠올리는 경우가 많다. 가비지 컬렉션 언어로 작성된 많은 게임들이 메모리를 직접 관리할 수 없다는 한계를 우회하려고 애쓰는 것은 흔한 일이다. 하지만 성능이 중요한 코드를 전혀 쓰지 않는다면, 이 단계를 넘어야 한다는 사실 자체를 알 가능성은 매우 낮다.
만약 이런 수백만 개의 개별 할당을 수동으로 관리해야 한다면, RAII나 “스마트” 포인터 같은 것을 권하는 이유도 이해된다. 컴파일러가 생성자와 소멸자를 자동으로 추가해주기 때문이다. 이 여정 단계에 있는 사람이라면, 보일러플레이트를 줄이고 뭔가를 빼먹어서 생기는 오류 가능성을 줄이니 좋은 일이라고 생각할 수 있다.
나는 이런 일을 하는 사람들이 멍청하다고 말하는 게 아니다. 전혀 아니다! 나도 프로그래밍 여정에서 이런 사고방식을 가진 적이 있고, 대부분의 프로그래머가 그렇다. 이는 자연스럽게 도달하기도 하고, 인터넷의 일반적인 프로그래밍 문화에서 배우기도 한다. 나는 소프트웨어를 출시해본 사람 중에 삶의 어느 시점에서든 이 단계에 있지 않았던 사람을 떠올릴 수 없다.
이 사고방식과 그로부터 나오는 해결책의 문제는, 결국 아키텍처의 결함을 땜질하고 있다는 점이다. 두 가지가 (아주 드문 경우를 제외하면) 서로 떼려야 뗄 수 없어서, 하나가 해제될 때 다른 것도 자동으로 해제된다면, 그것은 두 것의 수명이 결합되어야 한다는 의미다. 더 나은 접근은 그 모든 것들이 만들어지는 큰 “풀(pool)”/“아레나(arena)”를 하나 두고, 그 “풀”에서 나온 것들을 함께 해제하는 것이다.
개별 요소 사고방식 단계는 멍청하다—즉 이 사고방식에서 작성한 코드는 좋지 않겠지만, 당신이 멍청한 것은 아니다. 특히 모두가 어느 시점엔 이 단계에 있기 때문이다. 그 다음 단계는 _그룹화된 요소 사고방식(grouped-element mindset)_이다.
참고: 나는 매일같이 내가 하는 일들 때문에 스스로를 바보/얼간이/멍청이 같은 말로 부르니, 내가 “똑똑하다”고 주장한다고 생각하지 말라. 나는 대부분과 마찬가지로 똑같이 “멍청”하다. 멍청함은 멍청한 대로 행동한다.
개별 요소 사고방식에서 그룹화된 요소 사고방식으로 옮겨가면, 스마트 포인터와 RAII7는 대부분 중요하지 않게 된다. 이전 사고방식에서 겪던 종류의 소유권(ownership)과 수명(lifetime) 문제가 더 이상 큰 문제가 아니기 때문이다. 개별 요소 사고방식에서는 소유권이 지속적인 걱정거리이자 정신적 오버헤드다. 반면 그룹화된 요소 사고방식에서는, 대다수(99%+)의 경우 소유권이 명백하고(대개 사소하다).
이 그룹화된 요소 사고방식은 보통 다음을 중심으로 한다:
new/delete 또는 malloc/free의 사용을 매우 제한온라인에서 읽게 되는 대부분의 조언(과 비판)은 개별 요소 사고방식의 관점에서, 또는 그 관점을 위해 쓰인 것이다. 나는 목표가 그룹화된 요소 사고방식(그리고 그 이상)으로 가는 것이라고 주장하고 싶다. 이는 아직 프로그래밍을 충분히 잘하지 못하는 사람들에게 남아 있는 사고방식이다. 그들은 아키텍처 단계가 매우 제한적이며, 현대의 고성능이면서 잘 설계된 코드에는 적합하지 않다. 이는 오래된 사고 방식이지만 여전히 매우 만연해 있으며, 사람들은 이를 넘어야 한다. 나는 개별 요소 사고방식에서 작성된 “좋은 코드”는 없다고 주장한다. 일이 되긴 하겠지만 코드 자체는 “좋지” 않을 것이다. 기껏해야 적절(adequate)할 수는 있지만, 그 사고방식은 본질적으로 비효율적이고 버그를 만들기 쉽다.
참고: 많은 면에서 이 개별 요소 사고방식은 이 글의 “10억 달러짜리 실수”를 걱정하는 것보다 훨씬 더 비용이 크다. 성능 손실만으로도 업계 전체로는 아마 하루에 수십억 달러를 낭비하고 있을 것이다.
그룹화된 요소 사고방식은 다른 종류의 잘못된 메모리 주소(예: use-after-free)도 상당 부분 해결하는 경향이 있다. 큰 컬렉션을 생각하는 것만으로도 수명과 소유권이 훨씬 명확해지기 때문이다.
use-after-free는 보통 또 다른 나쁜 아키텍처 결정의 증상이다. 가비지 컬렉션이 없는 일부 언어들이 취하는 흔한 “해결책”은 언어에 더 복잡한 수명/소유권 의미론을 추가하는 것이다(예: Rust). 하지만 메모리 안전성이 최우선이 아니라면, 모든 것을 언어 차원에서 해결할 필요도 없고, 그럴 필요도 없다. 내 생각에 인터넷의 아무나가 주장하듯 모든 것이 시간적(temporal) 메모리 안전해야 한다고 생각하는 것은 또 다른 “실수”다. Odin은 공간적(spatial) 메모리 안전 문제를 이미 해결해주는 기능을 많이 갖고 있다. 다만 다른 인기 언어들처럼 시간적 메모리 안전을 언어 수준에서 “해결”하려 들지는 않는다. 컴파일 타임 강제, 런타임 검출, 아키텍처 설계 사이의 트레이드오프에 관한 문제다.
나는 개별 요소 사고방식이 많은 사람들이 OOP 방식의 프로그래밍을 여전히 사랑하는 이유이기도 하다고 주장하고 싶다. 아주 작은 조각만 생각하면 되고, 또 아주 작은 조각만 생각할 수 있기 때문이다. OOP는 프로그램 전체가 어떻게 동작하는지가 아니라 매우 작은 단위로 생각하도록 만든다. 이것은 Alan Kay 스타일 OOP(메시지 패싱)와 Java 스타일 OOP(상속 기반 캡슐화) 모두의 초기 목표이기도 했다.
조금 더 큰 관점에서 코드 안의 _시스템_을 생각하기 시작하면, 사물들이 어떻게 응집되고 결합되는지 훨씬 쉽게 볼 수 있다. 그룹화된 요소라는 시스템 수준에 도달하면, 다음 단계의 사고에 와 있는 것이다.
하지만 나는 (대부분의 사람에게는) 개별 요소 사고방식을 건너뛰고 바로 그룹화된 요소 사고방식으로 점프할 수는 없다고 생각한다. 이는 문제 내 데이터의 근본적 _단위(unit)_를 이해하는 데 필요한 사고 단계일 가능성이 크다. 개인에서 집단으로8.
Odin을 설계할 때, 나는 이런 사고방식의 구분을 명시적으로 염두에 두었다. 내 선택 대부분은 언어에서 무엇을 원하고 원하지 않는지에 대한 의식적인 것이었다. Odin의 설계 상당 부분은 장기적으로 좋지 않다고 느끼는 사고방식에서 벗어나 특정한 스타일로 “살짝 떠미는(nudging)” 것에 맞춰져 있다.
하지만 이런 설계 결정들은 많은 프로그래머가 가진 일반적인 _사고방식_과 충돌하기도 한다. Odin의 설계 결정에 동의하지 않는다면 괜찮다. Odin은 당신을 위한 언어가 아닐 수도 있다. 하지만 어떤 아이디어/기능/구성 요소를 다른 언어에 사소하게 끼워 넣고, 기대한 대로 “그냥” 작동할 거라고 생각하지는 말라.
언어 설계의 상당 부분은 이런 로컬한 결정을, 그것이 전역적인 가능성의 지형에 어떤 영향을 주는지까지 생각하는 일이다. 우리는 대부분의 시간에는 로컬하게 생각할 수 있어야 한다. 하지만 아키텍처 결정에 관해서는 가능한 한 전역적으로 생각해야 한다.
언어를 설계하는 것은 멋진 새 기능이나 문법 설탕(syntactic sugar) 같은 것을 추가하는 일만이 아니다. 당신이 설계하려는 문제 영역(problem space)에서 무엇을 달성하려 하는지, 그리고 개별 기능과 구성 요소들이 서로 어떻게 상호작용하며, 스케일이 커질수록 무엇을 개선하거나 무엇을 제한하는지 같은 결과까지 생각하는 것이다.
null 포인터 역참조는 경험적으로 런타임에서 잡기 가장 쉬운 종류의 잘못된 메모리 주소이며, 메모리 안전하지 않은 언어에서 발생하는 잘못된 메모리 주소 중에서도 가장 흔하지 않은 종류다. 그리고 사람들이 그것을 “문제”라고 생각하게 되는 원인은 특정한 종류의 개별 요소 사고방식에서 나온다.
정적 타입의 컴파일 언어이면서 수동 메모리 관리를 하는 언어에서는, 잘못된 메모리 주소 문제 전체 집합과 비교할 때 경험적으로 그리 큰 문제가 아니다. 그 대부분은 다른 사고방식으로 해결된다.
이 글의 2부에서 더 많은 내용을 명확히 다룬다: 10억 달러짜리 실수를 완화하기
C99에서 #define NULL ((void *)0), 또는 C++11과 C23의 nullptr.↩︎
역사적으로 Prime 50, CDC Cyber 180, 그리고 일부 Honeywell-Bull 머신은 0이 아닌 null을 가졌고, DG Eclipse MV, HP 3000, Lisp Machine, 일부 64비트 Cray도 그랬을 수 있다.↩︎
특정 최적화를 위해 필요하다면, x: T = ---로 초기화되지 않은 스택 메모리를 만들 수 있다.↩︎
혹시 눈치채지 못했다면, 내가 Odin을 다른 사람들에게 설명하려 할 때, C의 문제에 대한 선입견 때문에 Odin을 아예 묵살해버리려는 사람들이 있어서 이게 꽤 피곤하다. 이런 사람들 대부분은 대체로 진지하지 않지만, 소셜 미디어에서는 가장 시끄럽다.↩︎
내겐 주로 Odin을 시도하지 않으려는 핑계로 보인다. 괜찮다. Odin을 쓰든 말든 신경 쓰지 않는다. 하지만 핑계를 찾기보다는 그냥 당신에게 맞는 언어가 아니라고 솔직히 말하라.↩︎
“스마트”에 따옴표를 붙인 이유는, 그것들이 구조적으로도 “스마트”하지 않고 사용 방식도 “스마트”하지 않다는 점을 강조하기 위해서다.↩︎
명시적인 defer 문 같은 것이 유용하지 않다고 말하는 게 아니다. 다만 개별 요소의 수명을 암묵적으로 처리하는 RAII 스타일이 나쁘다는 것이다. 내가 defer가 나쁘다고 생각했다면 Odin에 추가하지도 않았을 것이다.↩︎
이것은 각주가 아니라 별도의 글이어야 할지도 모르지만, 조악한 비유로 이 그룹화된 요소 사고방식은 “공산주의”와 유사하고, 개별 요소 사고방식은 “자본주의”에 더 가깝다. “공산주의”가 악하다고 생각한다는 점은 밝혀둔다. 하지만 요소 기반 수명 사고와 시스템 기반 수명 사고의 관점에서 정치경제 체계의 비유가 꽤 잘 들어맞는다.↩︎