Edición global: lo siento chicos, me emocioné mucho y escribí muchas tonterías. Sólo un viejo geezer despotricando.
Quería creer que C se había salvado, pero desgraciadamente desde C11 se ha equiparado a C ++. Aparentemente, saber qué hará el compilador con los efectos secundarios en las expresiones requiere ahora resolver un pequeño acertijo matemático que involucra un ordenamiento parcial de secuencias de código basado en un "se ubica antes del punto de sincronización de".
Por casualidad, diseñé e implementé algunos sistemas integrados críticos en tiempo real en los días de K&R (incluido el controlador de un automóvil eléctrico que podría hacer que las personas se estrellaran contra la pared más cercana si el motor no se controlaba, un industrial de 10 toneladas robot que podría aplastar a las personas hasta convertirlo en una pulpa si no se ordena adecuadamente, y una capa de sistema que, aunque inofensiva, tendría unas pocas docenas de procesadores que absorberían su bus de datos con menos del 1% de sobrecarga del sistema).
Podría ser demasiado senil o estúpido para obtener la diferencia entre indefinido y no especificado, pero creo que todavía tengo una muy buena idea de lo que significa la ejecución concurrente y el acceso a datos. En mi opinión posiblemente informada, esta obsesión por los C ++ y ahora los chicos C con sus lenguajes de mascotas asumiendo los problemas de sincronización es un sueño costoso. O sabes lo que es la ejecución concurrente, y no necesitas ninguno de estos artilugios, o no, y le harías al mundo en general un favor sin tratar de meterse con él.
Toda esta carga de abstracciones de barrera de memoria que hacen agua la vista se debe simplemente a un conjunto temporal de limitaciones de los sistemas de caché de múltiples CPU, todos los cuales se pueden encapsular de forma segura en objetos de sincronización de SO comunes como, por ejemplo, los mutexes y las variables de condición C ++ ofertas.
El costo de esta encapsulación no es más que una caída diminuta en el rendimiento en comparación con lo que podría lograr el uso de instrucciones específicas de CPU específicas en algunos casos.
La volatile
palabra clave (o un#pragma dont-mess-with-that-variable
para todos, como programador del sistema, cuidado) hubiera sido suficiente para decirle al compilador que deje de reordenar los accesos a la memoria. El código óptimo se puede generar fácilmente con directivas asm directas para rociar el controlador de bajo nivel y el código del sistema operativo con instrucciones específicas de CPU específicas. Sin un conocimiento íntimo de cómo funciona el hardware subyacente (sistema de caché o interfaz de bus), de todos modos está obligado a escribir código inútil, ineficiente o defectuoso.
Un ajuste minucioso de la volatile
palabra clave y Bob habría sido todo el mundo menos el tío de programadores de bajo nivel más duro. En lugar de eso, la pandilla habitual de fanáticos de las matemáticas de C ++ tuvo un día de campo diseñando otra abstracción incomprensible, cediendo a su tendencia típica de diseñar soluciones en busca de problemas inexistentes y confundiendo la definición de un lenguaje de programación con las especificaciones de un compilador.
Solo que esta vez el cambio requirió desfigurar un aspecto fundamental de C también, ya que estas "barreras" tuvieron que generarse incluso en código C de bajo nivel para funcionar correctamente. Eso, entre otras cosas, causó estragos en la definición de expresiones, sin explicación o justificación alguna.
Como conclusión, el hecho de que un compilador pueda producir un código de máquina consistente a partir de esta pieza absurda de C es solo una consecuencia distante de la forma en que los chicos de C ++ hicieron frente a posibles inconsistencias de los sistemas de caché de finales de la década de 2000.
Se hizo un desastre terrible de un aspecto fundamental de C (definición de expresión), de modo que la gran mayoría de los programadores de C, a quienes no les importan los sistemas de caché, y con razón, ahora se ven obligados a confiar en los gurús para explicar el diferencia entre a = b() + c()
y a = b + c
.
Intentar adivinar qué será de este desafortunado conjunto es una pérdida neta de tiempo y esfuerzo de todos modos. Independientemente de lo que haga el compilador, este código es patológicamente incorrecto. Lo único responsable de hacerlo es enviarlo a la papelera.
Conceptualmente, los efectos secundarios siempre se pueden eliminar de las expresiones, con el esfuerzo trivial de permitir explícitamente que la modificación ocurra antes o después de la evaluación, en una declaración separada.
Este tipo de código de mierda podría haberse justificado en los años 80, cuando no se podía esperar que un compilador optimizara nada. Pero ahora que los compiladores se han vuelto más inteligentes que la mayoría de los programadores, todo lo que queda es un código de mierda.
Tampoco entiendo la importancia de este debate indefinido / no especificado. O puede confiar en el compilador para generar código con un comportamiento consistente o no puede. Si llama a eso indefinido o no especificado parece un punto discutible.
En mi opinión posiblemente informada, C ya es lo suficientemente peligroso en su estado K&R. Una evolución útil sería agregar medidas de seguridad de sentido común. Por ejemplo, al hacer uso de esta herramienta avanzada de análisis de código, las especificaciones obligan al compilador a implementar al menos para generar advertencias sobre el código de bonkers, en lugar de generar en silencio un código potencialmente poco confiable al extremo.
Pero en cambio, los chicos decidieron, por ejemplo, definir un orden de evaluación fijo en C ++ 17. Ahora, cada imbécil de software se incita activamente para poner efectos secundarios en su código a propósito, disfrutando de la certeza de que los nuevos compiladores manejarán ansiosamente la ofuscación de una manera determinista.
K&R fue una de las verdaderas maravillas del mundo de la informática. Por veinte dólares obtuviste una especificación completa del idioma (he visto a individuos solteros escribir compiladores completos simplemente usando este libro), un excelente manual de referencia (la tabla de contenido generalmente te indicará en un par de páginas de la respuesta a tu pregunta), y un libro de texto que le enseñaría a usar el lenguaje de una manera sensata. Completo con fundamentos, ejemplos y sabias palabras de advertencia sobre las numerosas formas en que podría abusar del lenguaje para hacer cosas muy, muy estúpidas.
Destruir esa herencia por tan poco beneficio me parece un desperdicio cruel. Pero de nuevo, bien podría dejar de ver el punto por completo. ¿Quizás algún alma amable podría señalarme en la dirección de un ejemplo de nuevo código C que aproveche significativamente estos efectos secundarios?