CSCI 3510
Spring 2004
Solutions to practice questions for quiz 4

  1. True or false?

    1. If Node is a type, then expression new Node[15] returns a pointer of type Node*. True. C++ does not distinguish between a pointer to one thing and a pointer to an array of things.

    2. If a C++ program does not return memory to the free space pool when it is done with the memory, then the system will periodically sweep through memory and return unused memory to the free space pool automatically. False. Memory management in the run-time stack is done automatically without scanning through memory when a function returns. Memory management in the heap is entirely up to the program, and is not automatic.

    3. A hash table is an excellent tool to help to sort an array. False. Hash tables mix things up, and are deliberately designed not to keep things in anything like sorted order.

    4. All data structures have a logical size that is determined when the program is compiled, and cannot change while the program runs. False. The components that are allocated in the heap and pointed to by an object are logically part of the object, and they can grow and shrink. Your assignment on hash tables is to design a data structure that grows as necessary.

    5. You are allowed to add an integer to a pointer in C++. True.

  2. Suppose that the following structure definition is given.

       struct Widget
       {
         int side;
         char* front;
         char bottom[22];
         Widget(int s, char* f, char *b)
         {
           side = s;
           front = f;
           strcpy(bottom, b);
         }
       };
    
    1. Show how to create a new widget called frodo with side = 4, front = "red" and bottom = "blue". Use the constructor.

      How you do this depends on where you want to store this new Widget. If you want it stored in the run-time stack, so that it will automatically be deallocated when the current function returns, use the following form.

          Widget frodo(4, "red", "blue");
          
      If you want it stored in the heap, so that it will only be deallocated when you explicitly deallocate it, use the following form. (This requires frodo to be a pointer to a Widget, of type Widget*.)
          Widget* frodo = new Widget(4, "red", "blue");
          

    2. Suppose that the constructor were not provided as part of the Widget structure type. Show how to create the same widget frodo without using the constructor.

      To create frodo in the run-time stack, use the following form.

             Widget frodo;
             frodo.side = 4;
             frodo.front = "red"
             strcpy(frodo.bottom, "blue");
          
      To create frodo in the heap, use the following form.
             Widget* frodo = new Widget;
             frodo->side = 4;
             frodo->front = "red"
             strcpy(frodo->bottom, "blue");
          

  3. The following C++ program compiles without errors, but when it is run, it causes a memory fault. Why?

        #include <iostream>
        using namespace std;
        int main()
        {
          char* word;
          cin >> word;
          cout << word << endl;
          return 0;
        }
    

    No memory is allocated for word. So the line cin >> word is using an uninitialized pointer, and tries to store a string at the memory address that the uninitialized pointer points to. You do not know where that will be.

  4. The following function is supposed to test whether two strings are the same, provided upper and lower case letters are treated as equal. That is, 'a' and 'A' should be considered the same letter. It is supposed to return 1 if the two strings are the same (when case is ignored) and 0 when they are not the same. For example, sameUpper("abc", "ABC") = 1, but sameUpper("abc", "AbD") = 0. This function works by copying each string, replacing lower case letters by upper case letters, and then using library function strcmp to tell whether the resulting strings are the same. (strcmp(s,t) returns 0 if strings s and t are equal, a negative number if s comes before t in alphabetical order, and a positive number if s comes after t in alphabetical order.) The function also uses function toupper, which converts a lower case letter to upper case, and returns all other characters unchanged. So toupper('a') = 'A', toupper('A') = 'A' and toupper(':') = ':'. There is a serious mistake in this function. Explain what the mistake is, and rewrite the function to avoid the mistake. You might need to make more than one change, but try to keep the spirit of the method instead of choosing a completely different method. Make sure the external behavior of the function is correct.

        bool sameUpper(char* x, char* y)
        {
          char *cpyx, *cpyy;
          int xlen = strlen(x);
          int ylen = strlen(y);
          int i;
          
          if(xlen != ylen) return 0;
          
          for(i = 0; i <= xlen; i++) {
            cpyx[i] = toupper(x[i]);
            cpyy[i] = toupper(y[i]);
          }
          
          if(strcmp(cpyx, cpyy) == 0) return 1;
          else return 0;      
        }
    

    No memory is allocated for the copies. Just before the for-loop, add

        cpyx = new char[xlen + 1];
        cpyy = new char[ylen + 1];
    
    But now that these arrays have been allocated, we want to deallocate them before returning. So replace the if-statement at the end by
          bool result;
          if(strcmp(cpyx, cpyy) == 0) result = 1;
          else result = 0;      
          delete [] cpyx;
          delete [] cpyy;
          return result;
      
    (Note. If you could change the method, it would be better to solve this problem without making copies of the strings. Just do the job as follows.
        bool sameUpper(char* x, char* y)
        {
          int i;
          
          for(i = 0; x[i] != '\0' && y[i] != '\0'; i++) {
            if(toupper(x[i]) != toupper(y[i])) return 0;
          }
          return x[i] == y[i];
        }
      
    This is shorter and more efficient. Moral: sometimes you do not need to make copies of things.)

  5. Write a function called stutter that takes a null-terminated string s as a parameter and produces, in newly allocated memory, the string that results from repeating each character in s twice. For example, stutter("abaac") = "aabbaaaacc". Stutter should return a pointer to the memory where the new string is stored.

    There are many correct solutions. Here is one.

        char* stutter(const char *s)
        {
          int   slen   = strlen(s);
          char* result = new char[2*slen + 1];
          
          for(int k = 0; k < slen; k++) {
            result[2*k]     = s[k];
            result[2*k + 1] = s[k];
          }
          result[2*slen] = '\0';
          
          return result;
        }
      
    NOTE: After writing a solution to this, you should try it by hand on an example. Do not just write it and presume that it works without trying it.

  6. What is the purpose of a destructor for a class? What is the destructor for class River called? Under what circumstances is the destructor called?

    A destructor recovers resources owned by an object. Typically, it deletes memory that the object owns, but that is physically outside the object.

    The destructor for class River is called ~River.

    The destructor is called any time an object is destroyed, for any reason.