3P by GN⁺ | ★ favorite | 댓글 1개
  • 새 C 표준에 맞춘 Modern C 개정판이 무료 다운로드로 공개되어, C 언어 학습·참조 자료를 C23 기준으로 다시 볼 수 있음
  • 개정의 중심은 C23 반영이며, ISO 출판 절차와 시점을 맞춰 새 표준 전환을 돕는 데 초점이 있음
  • 주요 컴파일러 새 릴리스가 C23 기능 대부분을 이미 구현해, 책의 변경점이 실험적 소개를 넘어 실제 사용 환경과 연결됨
  • _BitInt(N), nullptr, auto, typeof, constexpr 등 언어·라이브러리 변화가 폭넓게 반영되어 기존 C 코드 작성 방식에도 영향을 줌
  • 기존 플랫폼에서 바로 시도할 수 있도록 전환용 부록과 임시 include 헤더가 추가됐지만, Manning의 MEAP는 아직 진행 중임

무료 공개 자료와 표준 문서

  • Modern C C23 에디션은 무료로 다운로드할 수 있음
  • 책 전용 페이지에서 관련 자료를 함께 찾을 수 있음
  • 개정판은 여러 설명을 손봤지만, 핵심 목적은 새 C 표준인 C23을 반영하는 것임
  • 공개 접근 가능한 문서 중 새 표준 내용에 가장 가까운 자료는 N3220 PDF
  • 주요 컴파일러의 새 릴리스들은 C23이 가져오는 새 기능 대부분을 이미 구현함

C23 언어 변화와 전환 지원

  • 정수 관련 변화가 큰 비중을 차지함
    • 새 비트 정밀 타입 _BitInt(N) 추가
    • 오버플로 체크 산술과 비트 조작을 위한 새 C 라이브러리 헤더 추가
    • 현대 아키텍처에서 128비트 타입 가능성 반영
    • enum 타입의 실질적 개선 포함
  • C23의 다른 새 개념도 포함함
    • nullptr 상수와 그 기반 타입
    • attributes를 통한 구문 주석
    • autotypeof를 포함한 타입 제네릭 프로그래밍 도구
    • 가변 길이 배열에도 적용되는 {} 기본 초기화
    • 모든 타입의 이름 있는 상수를 위한 constexpr
  • 새 자료는 복합 표현식, 람다, “국제화”, 프로그램 실패에 대한 포괄적 접근까지 다룸
  • 기존 플랫폼에서 C23을 바로 시작할 수 있도록 부록과 임시 include 헤더가 추가됨
  • Manning의 새 에디션 MEAP는 아직 열려 있음

댓글과 토론

Hacker News 의견들
  • 내 머신의 저장 순서인 리틀 엔디언과 달리, 상위 자리 표현이 먼저 오는 방식은 빅 엔디언이라고 부르는데, 현대 프로세서에서 둘 다 흔히 쓰인다고 하기엔 s390x 말고는 거의 남지 않아서 좀 과장처럼 느껴짐
    이제 모두가 좋아하는 틈새/사장된 빅 엔디언 아키텍처 댓글이 곧 달릴 듯함
    • Arm은 바이 엔디언이고 대부분의 휴대폰에서 살아 있음
      다른 상위 댓글처럼, 현대적이라는 말이 꼭 인기 있거나 널리 쓰인다는 뜻은 아님
    • POWER는 바이 엔디언임. 최근 Linux on POWER는 리틀 엔디언이고, 예전에는 빅 엔디언 Linux on POWER가 흔했지만 몇 년 전 배포판들이 전환했음. 반면 AIX와 IBM i는 빅 엔디언임
      AIX와 IBM i가 IBM 메인프레임만큼 활발하진 않겠지만, AIX는 Solaris나 HP/UX보다도 더 살아 있다고 볼 수 있고, 예전의 수많은 상용 Unix와 비교하면 더 그렇다. IBM i도 간신히 버티는 중이지만, HP MPE처럼 벤더 지원이 공식 종료된 경쟁 레거시 미드레인지 플랫폼들보다는 훨씬 살아 있음
    • MIPS는 소비자용 네트워킹 하드웨어에서 아직 꽤 살아 있음
    • 어떻게 보면 모든 프로세서가 흔히 쓴다고도 할 수 있음. 네트워크 바이트 순서가 빅 엔디언이기 때문임
    • Sparc는 빅 엔디언 아닌가?
  • C의 가장 중요한 면은 이식성임. 작은 마이크로컨트롤러부터 거의 모든 컴퓨팅 플랫폼까지 가는 게 핵심인데, 새 C 버전이 그만큼 채택될지는 의문임
    최첨단을 쓰고 싶다면 C보다는 C++2x나 Rust를 고를 것 같음. 뭔가 놓치고 있나? 이 소위 현대적 C가 주는 이점이 무엇인지 궁금함
    • C 코드를 쓰는 장점 중 하나는 관용적 코드가 어떻게 생겨야 하는지, 어떤 언어 부분집합이 맞는지 같은 귀찮은 논쟁을 하지 않아도 된다는 점임
      최첨단이라면 Zig를 추천함. 현대 C++와 Rust보다 언어 복잡도가 훨씬 낮음. C23의 덜 눈에 띄는 좋은 부작용은 ... = {}{0} 같은 문법을 C++와 더 맞춰서, C 라이브러리 유지보수자가 C 코드를 C++ 컴파일러로 빌드하려는 사람들을 지원하기 덜 귀찮아진다는 것임
    • 과거에 C11, GNU 확장, 이제 C23에 들어간 개별 기능들이 그랬듯이 결국 채택될 것임. 예를 들어 이진수 표기 0b는 마이크로컨트롤러 세계에서 널리 쓰임
      마이크로컨트롤러 도구체인은 대체로 GCC 위에 만들어져서 기능을 공짜로 얻음. 항상 뒤처지는 독점 C 컴파일러도 있지만, 20년 전만큼 중요하지는 않음
    • 이런 기능들은 결국 주류로 흘러들 것임. 지금 C11이 그렇게 되고 있는 것과 비슷함
      임베디드나 아주 넓은 아키텍처 집합을 목표로 하지 않는다면 오늘부터 C23을 쓰지 못할 이유도 없음
    • thread_local 지정자는 이미 몇몇 마이크로컨트롤러 플랫폼에서 쓰이고 있지만, C11 이전에서는 완전히 불법이었음. 그래도 스레드 환경에서 메모리 관리를 크게 단순화함
      그걸 위해 굳이 C++ 세계로 들어가야 할 이유가 있나?
    • LLVM/GCC가 지원하는 대상이라면 새 C 버전도 자동으로 얻게 되지 않나? 다만 다른 아키텍처용으로 벤더가 수정한 낡은 GCC 도구체인에서는 못 받을 것임
  • 개인적으로 guard, defer, auto, constexpr, nullptr 같은 것들은 C를 훨씬 복잡하게 만듦. 단순함이 필요해서 C를 고르는 건데, 복잡함을 원하면 보통은 원하지 않더라도 C++를 고를 것이고, 차라리 Go나 서버라면 Elixir를 고르겠음
    _BitInt(N)도 못생겼고, 다행히 이제 bool이 된 _Bool이 떠오름. constexprnullptr는 C++ 냄새가 너무 강함. 그래도 Modern C는 훌륭한 책이고, 계속 붙잡고 쓸 생각인 C99 용도로 잘 써왔음
    • NULL에 뭐가 문제냐는 질문은 ISO 표준화의 몇 안 되는 장점 덕분에 관련 문서를 읽으면 답이 나옴: https://wg21.link/p2312
      요약하면, NULL 인자를 타입 제네릭 매크로에 넘길 때 놀라운 결과가 생길 수 있고, (1 ? 0 : NULL)(1 ? 1 : NULL) 같은 조건식의 지위가 NULL 정의 방식에 따라 달라짐. 또 포인터를 기대하는 가변 인자 함수에 NULL을 넘기면 심각한 결과가 날 수 있음. 요즘 많은 아키텍처에서 intvoid* 크기가 달라서, NULL이 그냥 0이면 잘못된 크기의 인자가 함수에 전달됨
    • 복잡도는 선형으로 늘지 않는다고 봄. 오히려 더 복잡한 도구가 과정과 최종 결과를 단순하게 만드는 일이 많음
      집을 짓는 것과 비슷함. 망치와 드라이버는 매우 단순하고 크레인은 극도로 복잡하지만, 집 짓기를 단순하게 만드는 건 크레인임. 망치와 드라이버만으로 집을 지으려면 엄청나게 복잡한 절차를 고안해야 함
      프로그래밍 언어도 마찬가지임. C++에서 제네릭 컨테이너를 만드는 건 사소하지만 C에서는 매우 어렵다. void *와 수동 캐스팅으로 어느 정도 흉내 낼 수는 있어도 번거롭고 오류가 나기 쉬우며 코드가 더 복잡해짐
      std::sortqsort도 마찬가지임. 템플릿과 함수 객체의 힘으로 구현이 더 단순하고 빠름. void *를 넘기고 실행 시점에 역참조하지 않아도 되고, 비교를 함수 정의 자체에 넣을 수 있음. 간접 호출도 없고 스택 전달도 없으며 비교 함수 인라인화까지 가능함. 언어 복잡도가 구현 복잡도를 뜻하지는 않음
    • auto는 타입 제네릭 매크로를 만질 때 주로 유용하지만, 일반 코드에서는 쓰지 않는 편이 좋음. 한동안 C++ 세계에서 유행했던 almost always auto 같은 광기는 피했으면 함
      안타깝게도 컴파일러마다 약간 차이가 있음. 기억으로는 Clang은 C++식 auto를 구현하고 GCC는 C식 auto를 구현해서, auto 포인터에서 미묘한 차이가 있었음. 그 차이가 지금은 고쳐졌는지는 모르겠음
      _BitInt(N)은 보통 직접 쓰지 않고 필요한 폭으로 typedef해서 씀. 예를 들면 typedef _BitInt(2) u2;처럼 씀. _B 같은 못생긴 문법은 밑줄 뒤 대문자 조합이 C 표준에서 예약되어 있어, 언어에 작은 기능을 추가할 때 기존 코드와 충돌하지 않기 위해 필요함. _Bool이라는 이름도 같은 이유임. 내가 알기로 defer는 실제로 C23에 들어가지 않았음
    • 예전 정의는 NULL이 포인터인지 정수인지조차 지정하지 않았음. 그래서 Posix의 ((void*)0) 요구를 따르지 않는 플랫폼에서는 포인터 타입도 크기도 아닌 발밑의 함정이었음
      constexprnullptr가 C++ 냄새가 나는 건 아마 C++에서 역수입됐기 때문임. 그래도 NULL은 계속 쓸 수 있는데, 겉보기에는 nullptr로 재정의된 듯함
    • 이 코드는 버그가 있고, 일부 아키텍처에서는 크래시가 날 수도 있음: execlp("echo", "echo", "Hello, world!", NULL);
      이 코드는 그 버그가 없음: execlp("echo", "echo", "Hello, world!", nullptr);
      이것도 괜찮음: execlp("echo", "echo", "Hello, world!", (char *)NULL);
  • 좋은 C 책 목록이 있는지 물어보려다 스스로 답을 찾음. 여기서는 _Modern C_를 중급으로 분류함
    https://stackoverflow.com/questions/562303/the-definitive-c-...
    • Modern C를 좋아하고 여러 곳에서 좋게 평가했음. 중급이라는 데 동의함
      K&R의 현대적 C 동반서로는 Ben Klemens의 21st Century C와 King의 C Programming: A Modern Approach가 더 접근하기 쉬운 대안이라고 봄
    • Christopher Preschern의 Fluent C: Principles, Practices and Patterns도 볼 만함
    • 이 목록은 완전하지 않음. 예를 들어 Effective C가 빠져 있음: https://nostarch.com/effective-c-2nd-edition
      개인적으로는 Modern C보다 Effective C가 더 좋음. Modern C는 매우 엄밀해서 전문가에게는 필요할 수 있는 언어 주석 달린 명세서를 읽는 느낌이고, 나처럼 가볍게 C를 쓰는 사람에게는 읽기 지루함
  • Metaware의 High C 확장 중 일부는 꽤 마음에 듦
    https://news.ycombinator.com/item?id=41647843
    https://news.ycombinator.com/item?id=38938402
  • 지난 1년 넘게 개인 프로젝트인 언어 인터프리터에 현대 C++를 쓰고 있는데, C++의 정신적 부담과 도구 문제 때문에 계속 C로 바꿀까 생각함. Visual Studio의 IntelliSense는 C++20 모듈을 쓰면 아직도 거의 동작하지 않고, 언어의 실패 때문에 너무 많은 것이 인터페이스로 밀려나 컴파일 시간도 보기 흉해짐
    반대로 클래스, 멤버 함수, 제네릭 프로그래밍, 이름공간에 너무 익숙해져 버려서 이미 걸려든 것 같기도 함
    • 오랫동안 C++를 써왔고, C로 넘어가려고 소멸자를 포기할 생각은 전혀 없음
      그 용도라면 C#을 고려해 봤나? Visual Studio는 C#과 훨씬 잘 맞음
  • macOS Preview에서 사이드바의 목차 링크를 클릭하면 제대로 동작하지 않음
    • zathura PDF 리더에서는 목차의 몇몇 링크를 테스트했는데 잘 동작함
    • 지금 목차는 확실히 깨져 있음
  • 유지 중인 라이브러리에서 C 컴파일러들이 모두 C99를 지원한다고 믿을 수 있게 된 지 몇 년밖에 안 됨: https://github.com/eyalroz/printf
    그런데 몇 년 지나니 어김없이 어떤 고대 임베디드 도구체인 때문에 C89 호환성을 요청하는 이슈가 열렸음. 그러니 C23은 좋긴 한데, 한 20년 뒤에 다시 얘기하자는 느낌임
  • 실무적으로 C가 왜 사실상 C99에 멈춰 있는지 설명하는 글을 누가 링크해 줄 수 있나? 이야기할 만한 프로젝트 중 C11 이후 기능을 활용하는 경우가 거의 없음
    • C99는 아직 새것임. Microsoft가 C++에도 있는 것 말고는 구현을 거부하면서 C를 죽이려 했음. MSVC는 C99 구현이 16년 늦었고, 최소한만 구현했음. C11 구현도 11년 늦었음
      수십 년 동안 C가 사실상 얼어붙어 있었기 때문에, 사용자층도 C가 있는 그대로인 걸 좋아하고 고대 잡동사니 컴파일러 지원을 꺼리지 않는 사람들로 자기선택된 듯함. 인내심을 잃었거나 21세기 언어를 원한 사람들은 C++/Rust/Zig 등으로 떠났음
    • Microsoft는 Visual Studio C 컴파일러에서 2015년쯤까지 C99 기능을 거의 구현하지 않으면서 사실상 C99를 방해했음. 그리고 2019년에야 실패를 인정하고 더 최근 C 버전 지원을 다시 시작했음. MSVC의 C 프런트엔드는 아직도 Clang과 GCC보다 안정적으로 뒤처짐
      2010년쯤에는 MSVC가 여전히 매우 중요했는데, 요즘 대부분의 개발자가 Linux로 옮긴 것처럼 보이는 관점에서는 이상하게 들림. 반면 C11 기능이 꼭 필요한 프로젝트도 많지 않음. C11은 C99에서 VLA를 가져가기도 했는데, 별로 아까운 손실은 아니었음. C23은 C99 이후 많은 C 코드베이스가 실제로 업그레이드할 만한 첫 버전일 수 있음