Portar a GCC 14: problemas del lenguaje C
La mayor conformidad de GCC 14 con los estándares modernos de C está convirtiendo advertencias de larga data —como `int` implícito, funciones no declaradas y algunos desajustes de punteros— en errores, lo que obliga a actualizar grandes bases de código y sistemas de construcción como autoconf. Los comentaristas sopesan las ventajas de una mayor seguridad, diagnósticos más claros y la adopción, muy retrasada, de características de C99/C23 (como los tipos de arreglos de longitud variable) frente al coste de romper C heredado y “portable” que dependía del comportamiento pre-ANSI o específico del compilador. El hilo también profundiza en cuestiones más básicas de diseño del lenguaje, incluidas la semántica de tipos enteros y de punteros, las convenciones de llamada y por qué algunas características de C evolucionaron tan lentamente pese a estar estandarizadas hace décadas.
Los valores predeterminados más estrictos de C en GCC 14
- GCC 14 convierte varias advertencias de C de larga data en errores (por ejemplo, declaraciones implícitas de funciones,
intimplícito y algunos usos de punteros incompatibles). - Muchos comentaristas consideran que esto llega tarde: estos constructos han sido efectivamente obsoletos durante décadas y casi nunca son intencionales en el código moderno.
- Otros señalan las roturas: el código antiguo (incluidas entradas de IOCCC y el userland histórico de GNU) fallará a menos que los errores se degraden. GCC sigue permitiendo banderas para restaurar el comportamiento antiguo.
Arreglos de longitud variable (VLAs) y C23
- El hilo discute C99 (VLAs obligatorios), C11 (VLAs opcionales) y C23 (los tipos VLA vuelven a ser obligatorios; los objetos VLA con almacenamiento automático siguen siendo opcionales).
- Se aclaró que los compiladores deben admitir la sintaxis y la semántica de los tipos de modificación variable (por ejemplo,
sizeofyoffsetofsobre ellos), pero aún pueden omitir los VLAs basados en la pila. - Debate sobre la complejidad de implementación: diseños dependientes del tiempo de ejecución y
typedefs introducen una maquinaria semántica adicional más allá de ser solo “azúcar sintáctico dealloca”. - MSVC todavía carece de VLAs y se saltó por completo C99, lo que limita el uso portátil de VLAs.
Trabajo de portabilidad en distribuciones
- Fedora y otras ya han hecho una limpieza a gran escala para prepararse para valores predeterminados más estrictos, muy afectados por pruebas generadas por autoconf que usan código C dudoso y ocultan advertencias.
- El riesgo principal era la “pérdida silenciosa de funciones”: las compilaciones tienen éxito, pero las comprobaciones de configure fallan, deshabilitando características. Esto llevó a enfoques de diferenciación de configuraciones.
- Que Clang/Xcode tuvieran valores predeterminados más estrictos y que Gentoo aportara parches al upstream ayudó a lograr consenso para endurecer GCC.
Construcciones C heredadas y modelos de compilación
- El C pre-ANSI permitía llamar a funciones no declaradas, tratándolas como si devolvieran
int; funcionaba “lo bastante bien” cuandosizeof(int) == sizeof(pointer). - Discusión sobre cómo los compiladores de una sola pasada manejaban funciones aún no declaradas y si una comprobación extra implicaría una “segunda pasada”.
- Algunos sugieren que el lenguaje debería admitir oficialmente una referencia adelantada más flexible, citando infraestructuras de compiladores más nuevas donde “simplemente funciona”.
Tipos enteros, tamaño de palabra y estilo
- Larga discusión sobre por qué
intsiguió siendo de 32 bits en sistemas de 64 bits: compatibilidad hacia atrás, escalera limitada de enteros estándar y supuestos existentes en el código. - Argumentos a favor y en contra de hacer alguna vez que
intsea de 64 bits: portabilidad frente a huella de memoria/caché frente a claridad. - Distinción entre tipos enteros “estándar” (
char/short/int/long/long long) y tipos enteros “extendidos”; se señala questdint.hsobre todo crea alias (typedef) de los primeros, no anchuras realmente nuevas. - Prácticas divergentes: algunos prefieren tipos de ancho fijo (
int32_t,int64_t), otros prefierenint+long longy evitan los tipos numerados por razones estéticas y de sobrecarga (en C++).
Punteros a funciones, void* y representaciones de punteros
- Se pregunta por qué una función que toma
const char*no puede usarse donde se espera un puntero a función de tipoint (*)(const void*, const void*). - El estándar prohíbe conversiones arbitrarias de punteros a función; C también carece de un tipo genérico de puntero a función.
- C11 garantiza que
void*y los punteros a caracteres comparten representación y alineación; otros tipos de puntero pueden no hacerlo, y los punteros a función pueden diferir de los punteros a objetos. - Se citan arquitecturas históricas y exóticas con tamaños de puntero y modos de direccionamiento no uniformes como razones por las que el estándar sigue siendo estricto.
Compatibilidad hacia atrás vs. casos de uso de nicho
- Algunos critican la dirección “anti-C89”, argumentando que eliminar
intimplícito perjudica usos creativos poliglota (por ejemplo, código dual C/JavaScript) sin un beneficio claro. - La opinión mayoritaria en el hilo favorece valores predeterminados más fuertes para evitar código sutilmente roto, con salidas de escape para programas heredados o de estilo concurso.