¿Así que crees que sabes C? (2016)
Un popular cuestionario en línea sobre C, basado en comportamiento indefinido y definido por la implementación, ha reavivado el debate sobre qué significa realmente “saber C”. Los comentaristas diseccionan casos límite relacionados con el tamaño de los enteros, el relleno de las `struct`, la codificación de caracteres, los desplazamientos y múltiples incrementos, discutiendo cuándo las respuestas son verdaderamente imposibles de saber sin especificar plataforma, compilador y versión del estándar. Muchos consideran estos acertijos pedantes o engañosos frente a preocupaciones del mundo real como la portabilidad, las pruebas en sistemas objetivo y evitar código ilegible, mientras que otros los defienden como recordatorios valiosos de lo frágiles que pueden ser las suposiciones de bajo nivel.
Reacción general al cuestionario
- Muchos ven el cuestionario como un ejercicio de “trampa” sobre los casos límite de C y el comportamiento indefinido, más que como una prueba práctica de habilidades.
- A algunos les gustó como recordatorio de las sutilezas de C; otros lo encontraron pedante, irritante o una “pérdida de tiempo” una vez que se dieron cuenta de que el metajuego era “la respuesta correcta es: no puedes saberlo”.
- Una queja recurrente: la opción de respuesta “No lo sé” es ambigua; la gente quería opciones explícitas como “indefinido”, “no especificado” o “definido por la implementación”.
Comportamiento indefinido / definido por la implementación / no especificado
- Varios comentarios desmenuzan si preguntas concretas implican comportamiento indefinido, definido por la implementación o no especificado.
- Hay desacuerdo directo sobre la primera pregunta: algunos la llaman indefinida, otros definida por la implementación, otros “meramente no especificada”, y algunos señalan que la explicación del artículo asume
sizeof(int) == 4, lo cual no está garantizado. - La disposición de la
struct, el ancho de los enteros, la signatura dechar, la codificación de caracteres (ASCII frente a EBCDIC) y los desplazamientos de ≥ ancho de bit se citan como ejemplos de donde el estándar deja deliberadamente un comportamiento no portable.
Discusiones técnicas concretas
- Promociones de enteros: los tipos más pequeños que
intse promocionan aintantes de la aritmética, lo que lleva a ubicaciones sorprendentes de UB (p. ej., ejemplos de multiplicación de 16 bits frente a 32 bits). - Desplazamientos: desplazar por ≥ el ancho del tipo es UB en C y C++; distintas CPU lo manejan de forma diferente (enmascarando, con cero, o haciendo trap).
- Expresiones con múltiples incrementos (
i++ + ++i, el intercambio XOR encadenado) se destacan como casos clásicos de comportamiento indefinido relacionados con los puntos de secuencia. - Bucles infinitos: C y C++ difieren en cuándo los compiladores pueden optimizar bucles sin efectos observables; esto importa para código embebido.
- El type punning basado en
uniones válido en C pero no está garantizado en C++.
Practicidad frente a pureza
- Un bando argumenta que en proyectos reales se apuntan plataformas conocidas, se confía en normas de facto (p. ej.,
intde 32 bits, ASCII, IEEE-754) y se respalda con pruebas; la conformidad estricta con el estándar en cada caso límite es excesiva. - Otro bando subraya que depender de un comportamiento no garantizado puede romperse con nuevos compiladores, banderas, arquitecturas o destinos embebidos; cuestionarios así resaltan suposiciones peligrosas.
Lenguajes, herramientas y alternativas
- La discusión deriva hacia las peculiaridades de C++, la sintaxis de casteo de D y el tamaño de enteros, los métodos de desplazamiento más seguros de Rust, Scheme vía Gambit-C y el uso de GC en C.
- Varios sostienen que hay mejores recursos educativos: buenas prácticas, gestión de memoria, idioms más seguros y libros (incluido el PDF enlazado desde el mismo sitio).