#include // FIND THE BUG // // This program asks the user for three integers and then // swaps values, if necessary, so that 'c' contains the // largest of the three original values. // // The code contains // // - poor coding practices, // - unnecessary operations, and // - a bug. // // Identify each of these problems and explain how you // would fix them. // // Finally, develop a collection of tests that would give // you confidence that the corrected program works. // Function declarations int main(); // Function definitions int main() { int a; int b; int c; std::cout << "Enter a number: "; std::cin >> a; std::cout << "Enter a second number: "; std::cin >> b; std::cout << "Enter a final number: "; std::cin >> c; if ( a >= b && a >= c ) { // Swap 'a' and 'c'. int b{ a }; a = c; c = b; } else if ( b >= c ) { // Swap 'b' and 'c'. int a{ b }; c = a; b = c; } std::cout << "The largest is " << c << std::endl; return 0; } // Poor coding practices // // There are several poor coding practices. // // First, the conditions do not use parentheses to make // the logical structure visually explicit. // // Instead of // // if ( a >= b && a >= c ) { // // write // // if ( (a >= b) && (a >= c) ) { // // This makes it immediately clear that the two operands // of && are // // (a >= b) // // and // // (a >= c). // // Similarly, // // else if ( b >= c ) { // // should be written as // // else if ( b >= c ) { // // There is only one comparison in this condition, so no // additional grouping is necessary. // Second, the variables 'a' and 'b' are declared in // main(), but variables with these same names are then // declared inside the bodies of the conditional // statements: // // int b{ a }; // // and // // int a{ b }; // // The inner declarations hide, or shadow, the variables // with the same names declared in the outer scope. // // This is legal C++, but it makes the code unnecessarily // confusing. // // These variables are intended to be temporary variables, // so give them a name that reflects their purpose: // // int tmp{ a }; // // or // // int tmp{ b }. // // Simply making this change makes the code easier to // inspect for errors. // With better variable names, the intended swaps become: // // if ( (a >= b) && (a >= c) ) { // // Swap 'a' and 'c'. // int tmp{ a }; // a = c; // c = tmp; // } else if ( b >= c ) { // // Swap 'b' and 'c'. // int tmp{ b }; // c = tmp; // b = c; // } // // Now examine these two supposed swaps carefully. // // Are both actually swaps? // Unnecessary operations // // Even after fixing the bug, some swaps may be // unnecessary. // // Consider // // a = 5 // b = 3 // c = 5. // // The condition // // (a >= b) && (a >= c) // // is true. // // The program therefore swaps 'a' and 'c', even though // they already contain the same value. // // There is no reason to perform this swap. // // Once we know that 'a' is at least as large as 'b', we // need to move 'a' into 'c' only when // // a > c. // // Thus, we could write // // if ( (a >= b) && (a > c) ) { // Should we also change // // a >= b // // to // // a > b? // // No. // // Consider // // a = 5 // b = 5 // c = 3. // // We must move either 'a' or 'b' into 'c'. // // If the first condition were // // (a > b) && (a > c), // // it would be false because a == b. // // Execution would then continue to the next condition. // // It is possible to design the cascade so that this still // works, but simply changing >= to > without considering // the following branch changes which branch must handle // ties. // // Keeping // // a >= b // // allows the first branch to handle the case where 'a' // and 'b' are tied for the largest value. // There is a similar unnecessary operation in the second // branch. // // Consider // // a = 3 // b = 5 // c = 5. // // The first condition is false because // // a >= b // // is false. // // The condition // // b >= c // // is true, so the program attempts to swap 'b' and 'c' // even though both contain 5. // // There is no reason to perform this swap. // // Therefore, once the first condition has failed, the // second condition only needs to be // // b > c. // The bug // // Consider the first swap: // // int tmp{ a }; // a = c; // c = tmp; // // This is a valid swap. The original value of 'a' is // saved in 'tmp' before 'a' is overwritten. // // Now consider the second: // // int tmp{ b }; // c = tmp; // b = c; // // Suppose // // b = 5 // c = 2. // // After // // int tmp{ b }; // // we have // // tmp = 5 // b = 5 // c = 2. // // After // // c = tmp; // // we have // // tmp = 5 // b = 5 // c = 5. // // Finally, // // b = c; // // assigns 5 to 'b', which already contains 5. // // The original value 2 stored in 'c' has been lost. // // This is not a swap. // To swap 'b' and 'c', first save the original value of // 'b', then assign the original value of 'c' to 'b', and // finally assign the saved value to 'c': // // int tmp{ b }; // b = c; // c = tmp; // // The corrected code is therefore: // // if ( (a >= b) && (a > c) ) { // int tmp{ a }; // a = c; // c = tmp; // } else if ( b > c ) { // int tmp{ b }; // b = c; // c = tmp; // } // // Notice that we do not need to completely sort the three // values. The only requirement is that 'c' contain the // largest value. // Writing tests // // How can we test the corrected program? // // There are three important kinds of input: // // - all three values are equal, // - exactly two values are equal, and // - all three values are different. // // For the last two cases, we should try the different // possible positions of the values. // // The following tests cover these possibilities: // // Input Expected result // a b c for c // // All three equal 1 1 1 1 // // Smaller duplicated 1 1 2 2 // 1 2 1 2 // 2 1 1 2 // // Larger duplicated 1 2 2 2 // 2 1 2 2 // 2 2 1 2 // // All different 1 2 3 3 // 1 3 2 3 // 2 1 3 3 // 2 3 1 3 // 3 1 2 3 // 3 2 1 3 // // Because the program promises only that 'c' contains // the largest value, these tests should primarily verify // the final value of 'c'. The values left in 'a' and 'b' // need not be in sorted order. // Here is one corrected solution: /* int main() { int a{}; int b{}; int c{}; std::cout << "Enter a number: "; std::cin >> a; std::cout << "Enter a second number: "; std::cin >> b; std::cout << "Enter a final number: "; std::cin >> c; if ( (a >= b) && (a > c) ) { int tmp{ a }; a = c; c = tmp; } else if ( b > c ) { int tmp{ b }; b = c; c = tmp; } std::cout << "The largest is " << c << std::endl; return 0; } */