Resumen
Esta clase cierra la tercera unidad del curso, dedicada a las expresiones y sus operadores. Reconstruye la cadena de conceptos que va del operador aritmético a la función de biblioteca, pasando por el árbol de la expresión y el tipo de cada nodo, las condiciones y su álgebra, el cortocircuito, el efecto secundario y el orden de evaluación, y los bits; separa lo que de ello vale en cualquier lenguaje de lo que es propio de C++; reúne el comportamiento indefinido de la unidad, con lo que detecta el compilador, lo que detecta el detector y lo que no detecta nadie; repite en su evaluación de salida las destrezas de la entrada; y termina con seis desafíos que combinan la unidad entera.
Objetivos de aprendizaje
- Predecir el valor, el tipo y el efecto de cualquier expresión del curso, a partir de su árbol y de las conversiones en cada nodo.
- Escribir y negar condiciones compuestas, y usar el cortocircuito para proteger una operación que de otro modo carecería de significado.
- Reconocer las expresiones que carecen de significado (el catálogo del comportamiento indefinido de la unidad), decir qué herramienta detecta cada una y reescribirlas sin él.
- Elegir entre la aritmética, los operadores de bits y la biblioteca matemática para un cálculo dado, y justificar la elección.
- Separar, en lo aprendido en la unidad, lo que vale en cualquier lenguaje de lo que es propio de C++.
Lo logrado en la unidad
La unidad se propuso enseñar a construir expresiones cuyo valor se pueda predecir con certeza: qué operación se hace primero, de qué tipo es el resultado, qué efectos secundarios tiene y qué expresiones no tienen significado. El propósito se ha cumplido apoyándose solo en lo que la introducción declaró por sabido: los tipos de la unidad 2, sus rangos y sus conversiones. El programa de las seis preguntas con que la introducción planteó el problema puede ahora leerse como lo lee el compilador, y corregirse expresión por expresión.
Aritmética
La clase Aritmética estableció el comportamiento exacto de los cinco operadores aritméticos. La división entera trunca hacia el cero, y el resto toma el signo del dividendo, de modo que se cumple siempre la identidad (a / b) * b + a % b == a; de ahí que -7 / 2 valga y -7 % 2 valga , y que la imparidad se pregunte con n % 2 != 0. Lo relacionó con el resto euclídeo, ((a % b) + b) % b para , y usó ambos para descomponer cantidades. Enseñó a dibujar el árbol de una expresión según la precedencia y la asociatividad, y a decidir el tipo en cada nodo por las conversiones aritméticas habituales. Cerró con los cocientes sin significado (la división entera por cero y el menor entero entre ) y con el desbordamiento de un resultado intermedio, que basta para el comportamiento indefinido aunque el resultado final quepa.
Relaciones y lógica
La clase Relaciones y lógica enseñó que una condición es una expresión de tipo bool, con su lugar en el árbol: las relaciones ligan menos que la aritmética, && más que ||, y ! más que todas. Enseñó a traducir enunciados a condiciones y a reconocer las que compilan con otro significado, como 1 < x < 5 y day == 6 || 7. Mostró que && y || evalúan su segundo operando solo si el primero no decide, y que ese cortocircuito permite proteger una operación con una guardia a su izquierda. Terminó con las leyes de De Morgan, que niegan una condición compuesta, y con su límite: con un valor no numérico, !(a < b) no equivale a a >= b.
Asignación, efectos secundarios y comportamiento indefinido
La clase Asignación, efectos secundarios y comportamiento indefinido distinguió el valor de una expresión de su efecto: la asignación es una expresión que vale lo asignado y cambia una variable, y x++ vale el valor anterior mientras ++x vale el nuevo. Mostró que el árbol de una expresión no fija el orden en que se calculan sus ramas, de modo que dos efectos sobre una misma variable, o un efecto y una lectura, sin orden entre ellos, dejan la expresión sin significado, y precisó lo que C++17 ordenó y lo que dejó sin especificar. Reunió el catálogo de los cuatro grados (definido, definido por la implementación, no especificado e indefinido), explicó que el compilador razona suponiendo que el programa no tiene comportamiento indefinido, y dijo, para cada caso del catálogo, qué herramienta lo detecta.
Bits y biblioteca matemática
La clase Bits y biblioteca matemática enseñó que un entero es también una sucesión de bits: &, | y ^ aplican una conectiva a cada columna, ~ invierte todos los bits, y los desplazamientos multiplican y dividen por potencias de 2, con la condición de que la cantidad desplazada no sea negativa ni alcance el ancho del tipo. Con ellos construyó las máscaras que consultan, encienden, apagan y alternan un bit. Advirtió que estos operadores ligan menos que las relaciones, de modo que x & 1 == 0 no pregunta lo que parece, y que ^ no es la potencia. Cerró con la biblioteca: <cmath>, <numbers>, <numeric> y <bit> dan cálculos correctos y probados, con sus propias precondiciones, como la del valor absoluto, que no existe para el menor entero.
Cargando el contenido…