C에서 sizeof가 놀라울 정도로 파싱하기 어려운 이유

3 hours ago 1
  • C의 sizeof 피연산자는 단항 표현식 또는 괄호로 감싼 타입 이름이며, 타입 이름에만 괄호가 필수임
  • 여는 괄호 뒤에서 타입 이름 파싱을 먼저 시도하고 실패 시 되돌리는 방식은 복합 리터럴 (int){0} 때문에 충분하지 않음
  • sizeof(int){0}는 (int){0} 전체를 표현식으로 처리하는 유효한 C 코드이며, 뒤에 .x[0]() 같은 후위 연산자가 여러 개 이어질 수 있음
  • 파서는 복합 리터럴 뒤의 후위 연산자를 모두 처리하거나, 역추적 없이 단항 표현식과 괄호로 감싼 타입 이름을 판별해야 함
  • 단항 표현식과 캐스트 표현식 파싱을 무리하게 합치면 sizeof(int)+1을 잘못 해석할 수 있으며, 같은 문제는 C2y의 _Countof 에도 적용됨

두 가지 피연산자 형태

  • sizeof는 표현식과 타입 이름을 서로 다른 문법으로 받음
    • sizeof 67, sizeof(67)은 표현식이므로 괄호가 필수가 아님
    • sizeof(int)처럼 타입 이름을 사용할 때는 괄호가 필요함
    • sizeof (x).y처럼 괄호로 감싼 표현식 뒤에 멤버 접근이 이어질 수도 있음
  • 단순한 파서는 여는 괄호를 만나면 다음 토큰부터 타입 이름으로 파싱하고, 실패하면 괄호를 토큰 스트림에 되돌린 뒤 표현식으로 처리할 수 있음

복합 리터럴이 만드는 예외

  • sizeof(int){0}에서 (int){0}은 복합 리터럴 표현식이므로 유효한 C 코드임
  • 타입 이름의 닫는 괄호 뒤에 {가 있는지만 검사하는 특수 처리로는 부족함
    • sizeof(T){}.x[0]()처럼 복합 리터럴 뒤에 임의 개수의 후위 연산자가 이어질 수 있음
  • 파싱 전략은 두 가지로 나뉨
    • 복합 리터럴을 인식한 뒤 이어지는 후위 연산자를 모두 파싱함
    • 단항 표현식과 괄호로 감싼 타입 이름 중 하나를 역추적 없이 판별하는 함수를 작성함
  • 단항 표현식과 캐스트 표현식 파서를 하나로 합칠 때는 sizeof(int)+1을 주의해야 함
    • 이는 캐스트 표현식의 크기를 구하는 형태가 아니라 덧셈 표현식
  • C2y에서 새로 도입된 _Countof 에도 같은 파싱 문제가 적용됨
Read Entire Article