3.10 Exercises
Write itoh() (Listing 3.3 on page 91) without recursion, and use the same main() program from the chapter. What are the advantages and disadvantages of each version?
The recursive version of itoh() (Listing 3.3 on page 91) and Exercise 1 both have void return values because each routine displays the result. Modify the recursive version of itoh() to return a character string pointer to the caller. Use the following program to test your solution.
Listing 3.21 Program with integer-to-hexadecimal conversion
// itoh2.C - recursive integer-to-hex conversion, returns buffer #include <iostream.h> #include <stdlib.h> int main(int argc, const char *argv[]) { unsigned int num; char *itoh(unsigned int); // returns character pointer if (argc == 1) { cerr << "Usage: " << argv[0] << " number" << endl; return 1; } num = atoi(argv[1]); cout << itoh(num) << endl; // conversion routine return 0; } $ itoh2 23456 5ba0
Write a function called nextday(), using function prototypes, const declarations, and references where applicable. Here's a program to test nextday().
Listing 3.22 Tomorrow's date
// nextday.C - determine tomorrow's date #include <iostream.h> struct data { int month, day, year; }; int main() { static date d1 = { 7, 30, 1992 }; static date d2 = { 12, 31, 1989 }; static date d3 = { 2, 28, 1992 }; date next; next = nextday(d1); cout << next.month << ',' << next.day << ',' << next.year << endl; next = nextday(d2); cout << next.month << ',' << next.day << ',' << next.year << endl; next = nextday(d3); cout << next.month << ',' << next.day << ',' << next.year << endl; } $ nextday 7,31,1992 1,1,1990 2,29,1992
Should nextday() modify its date argument or not? What effect does this have on your design?
Write functions rol() (rotate left) and ror() (rotate right) that rotate 32-bit integers. The first argument to both functions is an integer to rotate, and the second argument is the number of bits to rotate. Provide a way to embed (cascade) rol()and ror() calls and handle illegal shift counts.
Examine the following C++ program.
Listing 3.23 Join strings
// joins.C - joins strings with new/delete operators #include <iostream.h> #include <string.h> int main() { try { char *join(const char*, const char*); char *joinb(const char*, const char*); cout << join("alpha","bet") << ' '; cout << joinb("duck","soup") << endl; } catch (char *msg) { // catch handler cerr << msg << endl; return 1; } return 0; } $ joins alphabet duck soup
Write the join() and joinb() routines to concatenate character strings. Function joinb() inserts a blank between its two arguments. Have both routines allocate memory dynamically with operator new and return free store pointers back to the caller.
Who is responsible for deleting the free store memory here? Is there a memory leak? How would you fix this problem?
Examine the following namespace definitions.
namespace Black { // define namespace Black void print(int k); } namespace White { // define namespace White void print(int k); } using Black::print; // global using declaration - OK? void sub1() { using White::print; // local using declaration print(5); // which print()? } void print(int k) { if (k > 0) print(k-1); // which print()? }
Can you identify which print() functions are called? What do you think the scope rules are here? Why is the global using declaration a problem?
Examine the following C++ statements.
char *ps1 = "hello"; char **ps2 = new char *("hello");
In terms of storage classes, describe the differences between these two statements. What are the advantages and disadvantages of these statements in a program?
In Listing 3.19 on page 161, why can't the bsize tunable parameter be a default argument in bmove()?
Write functions called ndim() and nfree() that allocate and free multidimensional arrays of any dimension and any built-in data type. Here are the function prototypes you need for ndim() and nfree().
void ndim(void *, ...); // allocate the array void nfree(void *, int); // deallocate the array
Use Standard Args, since ndim() takes a variable number of arguments (see "Functions with a Variable Number of Arguments" on page 104). Here are examples of how you call ndim() and nfree().
double **a; // a[i][j] is a double ndim(&a, 2, 3, 4, sizeof(double))); int ***b; // b[i][j][k] is an int ndim(&b, 3, 4, 5, 6, sizeof(int))); nfree(a, 2); // deallocate 2D array of doubles nfree(b, 3); // deallocate 3D array of integers
The prototypes for ndim() and nfree() specify void * for their first argument. This allows calls with generic pointers having any number of indirections (**, ***, etc.). Note that you call ndim() with the address of a generic pointer, making it possible for ndim() to modify the pointer with the address of memory that it allocates dynamically. The second argument of ndim() is the number of dimensions that follow, and its last argument is the size of each array element in bytes.
Hint: Use a recursive approach for the design of ndim() and nfree().