C 언어에서 변수나 타입의 크기를 알려주는 `sizeof` 연산자는 겉보기와 달리 컴파일러가 구문 분석(파싱)하기에 상당히 복잡한 요소입니다. 그 이유는 `sizeof`의 피연산자가 두 가지 형태를 가질 수 있기 때문입니다. 하나는 `sizeof 67`처럼 일반적인 단항 표현식이고, 다른 하나는 `sizeof(int)`처럼 괄호로 감싼 타입 이름입니다. 문제는 타입 이름에만 괄호가 필수라는 점이며, 이로 인해 파서는 괄호를 만났을 때 다음 내용이 표현식인지 타입 이름인지 명확히 구분하기 어렵습니다.
이러한 복잡성은 특히 C99 표준에서 도입된 복합 리터럴(compound literal)과 결합될 때 두드러집니다. 예를 들어 `sizeof(int){0}`와 같은 코드는 `(int){0}` 전체를 하나의 복합 리터럴 표현식으로 간주하는 유효한 C 코드입니다. 여기서 `(int)`는 타입 이름처럼 보이지만, 뒤에 `{0}`가 붙으면서 전체가 표현식이 됩니다. 게다가 `sizeof(T){}.x[0]()`처럼 복합 리터럴 뒤에 멤버 접근(`.x`), 배열 첨자(`[0]`), 함수 호출(`()`)과 같은 임의의 후위 연산자가 여러 개 이어질 수 있어 파싱은 더욱 복잡해집니다. 파서는 괄호 뒤의 내용을 타입 이름으로 먼저 시도했다가 실패하면 되돌려 표현식으로 처리하는 단순한 방식으로는 이러한 복합 리터럴을 제대로 처리할 수 없습니다. 따라서 복합 리터럴과 그 뒤에 이어지는 모든 후위 연산자를 정확히 인식하거나, 역추적 없이 단항 표현식과 타입 이름을 판별하는 정교한 전략이 필요합니다.
이러한 `sizeof` 파싱의 복잡성은 C 언어의 역사적 배경과 밀접하게 관련되어 있습니다. C는 처음부터 형식 문법에 기반하여 설계되기보다는, 당시의 컴파일러가 받아들이는 코드를 문법으로 삼는 경향이 있었습니다. 1989년 C 표준화 과정에서는 기존의 다양한 컴파일러가 처리하던 코드를 모두 수용해야 했고, 초기 컴파일러의 제한적인 메모리 환경 때문에 추적해야 할 상태를 줄이는 편법들이 문법에 반영되기도 했습니다. 오늘날이라면 식별자가 타입인지 변수인지에 따라 구문 트리가 달라지는 문법은 피하겠지만, 당시에는 구문 분석 시점에 이미 의미 정보(심볼이 타입인지 변수인지)를 활용할 수 있었기에 이러한 기능들이 계속 추가될 수 있었습니다. 이 때문에 `sizeof`와 같은 연산자를 정확히 파싱하려면 단순히 문법 규칙만으로는 부족하고, 현재 스코프의 의미 정보(어떤 심볼이 타입 이름인지)까지 파악해야 하는 난점이 발생합니다. 이는 C 언어가 흔히 '단순한 언어'로 불리면서도 실제로는 분석하기 까다로운 '우발적 복잡성'을 많이 내포하고 있음을 보여주는 사례라고 할 수 있습니다.