25-Minute Workout Part II Answers 1 a. This is the list that I came up with: * If the argument is an expression with an operator of lower precedence than multiplication, the results will be wrong because x is not surrounded by parentheses. For example: TWO_MAX(3 + a, b) ==> 3 + a > b ? (2 * 3 + a) : (2 * b) which, given the precedence of the operators, is interpreted as TWO_MAX(3 + a, b) ==> ((3 + a) > b) ? ((2 * 3) + a) : (2 * b) Notice the appearance of (2*3)+a instead of the intended 2*(3+a). * If the macro is involved in an expression with operators of the same or higher precedence, the results will be wrong because the expression is not surrounded by parentheses. Consider the following example: a = 2 * TWO_MAX(b, c); ==> a = 2 * b > c ? b : c; which is interpreted as a = (2 * b) > c ? b : c; * If the macro is invoked with an argument that has side effects, the side effects will “take effect” twice because x appears more than once. This is demonstrated in the following snippet a = TWO_MAX(b++, c) ==> a = b++ > c ? b++ : c; If b++ is greater than c, b is actually incremented twice. b. These problems can be corrected by making the macro an inline function: //twoMax - implement as an inline function inline twoMax(int x, int y) { return x > y ? 2 * x; 2 * y; } This inline version does not suffer from any of the problems mentioned previously. 2 As with most profound questions of our time, the answer is in the question. It is possible to overload the function with another function of the same name, which can then turn around and call our function with the default value. You can even make this ersatz function inline if you’re really worried about performance overhead: #include void aFunc(int x, float y, char z) { cout << “x = “ << x << “, y = “ << y << “, z = “ << z << “\n”; } inline void aFunc(int x, char z) { aFunc(x, 0.0, z); } int main() { aFunc(1, 2.0, ‘3’ ); //calls the function aFunc(4, ‘5’ ); //calls the ersatz function return 0; } Executing the simple test program generates the following results: x = 1, y = 2.0, z = 3 x = 4, y = 0.0, z = 5 3 Here are how the calls match up: a. Calls 1; this is straightforward b. Calls 2; a constant character string is of type char* c. Calls 3; any pointer type can be converted to void* d. Calls 1; a character can be promoted to an int e. Ambiguous between 4 and 5; the int could be converted to either 4 Without a declaration to tell it otherwise, C++ would assume that printf() is a C++ function and attempt to mangle the name. The stdio.h include file includes extern “C” declarations to make sure that C++ knows that this is a C function. 5 My solution using printf() and scanf() I/O follows: #include void fn(int &x, int &y) { printf(“Enter x & y:”); scanf(“%d%d”, &x, &y); } void main() { int i, j; fn(i, j); printf(“i = %d, j = %d\n”, i, j); } Notice that you still have to pass the address of x and y to scanf() because it knows nothing about referential arguments. The remainder of both the calling and called functions are simpler. The stream I/O solution makes no direct reference to pointers: #include void fn(int &x, int &y) { cout << “Enter x & y:”; cin >> x >> y; } void main() { int i, j; fn(i, j); cout << “i = “ << I << “, j = “ << j << “\n”; } 6 Here is my solution: Davis multiply(Davis&, Davis &); Davis multiply(Davis &, float); Davis multiply(float, Davis &); float multiply(float, float); Notice that Davis, float must be listed twice: the first time with Davis first and the second time with float first. There is no association between the two functions. (One of the functions can be an inline function that calls the other, so it’s not a big deal.) 7 My solution is as follows: #include int main() { cout << “Hello, world\n”; return 0; } Notice that stdio.h has been replaced by iostream.h. This is critically important. Without iostream.h, C++ will not recognize << as the insertion operator.