C23: Un C ligeramente mejor

C23, la última revisión del estándar del lenguaje C, introduce un conjunto de funciones incrementales como deducción de tipos con `auto`, `typeof`, una forma de palabra clave de `static_assert`, una sintaxis de inicialización más limpia y la eliminación de construcciones heredadas como los trigraphs y las definiciones de funciones al estilo K&R. Los comentaristas están divididos sobre si estos cambios mejoran realmente C: algunos celebran mejores macros genéricas, aritmética comprobada más segura y una mayor alineación con extensiones existentes de compiladores y con C++, mientras que otros los ven como retoques superficiales que no abordan problemas de larga data como las cadenas, los módulos y una gestión de recursos más segura. La conversación también sitúa a C23 en un panorama más amplio en el que muchos proyectos nuevos de sistemas prefieren C++ o Rust, y donde lenguajes alternativos (Zig, D, Nim, Ada, etc.) compiten por ofrecer una interoperabilidad con C más segura o más ergonómica.

Auto, typeof y “genéricos” en C23

  • Reutilizar auto es visto por algunos como una elección extraña; muchos esperan que sea una norma de estilo “prohibida” en código C serio, a diferencia de C++.
  • Otros sostienen que en C sí es realmente útil:
    • Evita nombres de tipo verbosos y los prefijos struct/enum.
    • Ayuda con macros genéricas (p. ej., SWAP(a,b) usando auto tmp = a;) y con tipos enteros de ancho fijo.
    • Estandariza las extensiones existentes __auto_type.
  • También se añade typeof; algunos señalan que ahora se puede escribir const typeof(var1) tmp = var1;.
  • Debate sobre si _Generic realmente cuenta como “genéricos”: algunos lo ven como sobrecarga/un switch sobre el tipo, no como estructuras de datos paramétricas; otros dicen que aun así alcanza el umbral de “genérico”.

Otros ajustes de lenguaje en C23

  • static_assert pasa a ser una palabra clave; C11 ya tenía _Static_assert y una macro, pero ahora se puede usar sin <assert.h>.
  • func() ahora es explícitamente equivalente a func(void), y se permiten parámetros sin nombre.
  • La inicialización de struct con {} (zero-init) ahora es válida (struct foo x = {};), alineándose con C++.
  • La eliminación de los trigraphs y de las antiguas declaraciones de funciones al estilo K&R es bienvenida como limpieza de rarezas históricas.
  • Algunos desean que C23 hubiera incluido una función integrada tipo defer/RAII; existe una propuesta así, pero sigue siendo “exploratoria”.

Soporte de toolchain y compiladores

  • La referencia a cppreference muestra que GCC 13 tiene un soporte de C23 comparativamente amplio; Clang va rezagado en varias características.
  • Las explicaciones ofrecidas: el diseño modular de LLVM y los forks corporativos ralentizan la evolución del frontend; grandes contribuidores se centran en backends o en otros lenguajes.
  • Se informa que Pelles C soporta casi todas las características de C23, incluido #embed.
  • MSVC históricamente descuidó C, pero más recientemente soporta C17 (salvo algunas características opcionales de C99); el momento en que llegue C23 no está claro.

C vs C++ y filosofía del lenguaje

  • Algunos argumentan que C++ ya es “un mejor C” y que C23 es sobre todo una retroportación de características de C++.
  • Otros responden que C y C++ ahora sirven a dominios distintos; la complejidad de C++, la metaprogramación y la generación de código oculta se ven como inadecuadas para algunos casos de uso embebidos o de bajo nivel.
  • Las analogías varían: desde “C++ es un cybertruck frente a la bicicleta de C” (criticada como inexacta) hasta “C son herramientas manuales, C++ añade herramientas eléctricas”.
  • Debate sobre excepciones y RAII en sistemas embebidos: algunos dicen que RAII es sencillo y útil; otros se preocupan por ciclos de vida de objetos menos explícitos.

Cabeceras, módulos y superficie de API

  • Algunos desearían que C tuviera módulos/imports reales en lugar de cabeceras, para evitar duplicación y acelerar compilaciones, posiblemente con import junto al legado #include.
  • A otros les gusta la separación de cabecera/implementación de C para exponer claramente una API pública; señalan que esto es ortogonal a si el lenguaje tiene o no un sistema de módulos.
  • Se comenta que unos módulos más fuertes podrían romper parte de la interoperabilidad C/C++, lo cual es políticamente sensible.

Alternativas con seguridad de memoria y FFI con C

  • Un largo subhilo explora “lenguajes pequeños, estables y seguros en memoria con excelente FFI con C y sin una gran pérdida de rendimiento”.
  • Entre los candidatos mencionados están Zig, D, Rust, Nim, V, Vala, LuaJIT, Ada, varios Schemes y Lisps, Swift, Julia y Go.
  • Compromisos destacados:
    • La verdadera seguridad de memoria suele implicar GC o borrowing al estilo Rust; ambos complican el FFI y/o la simplicidad.
    • Un FFI con C fácil y de sobrecoste cero tiende a debilitar las garantías de seguridad fuertes (los errores pueden colarse desde C).
    • Varios comentaristas sostienen que la lista de deseos exacta puede ser imposible de satisfacer por completo; como mucho se puede aproximar con distintos compromisos.