CSCE 2004 - Laboratory Assignment 15

The objective of this lab is to know more about pointers in C++ and dynamic allocation of memory. This lab assignment has the following steps:

  1. Pointer Declaration
  2. Your program's variables and instructions when loaded into memory occupy a certain range of memory. We can find out the address of a variable using the '&' operator. For example,

    	int var1=20;
    	cout << "The address of var1 is " << &var1 << endl;
    
    
    

    To increase your programming power, you need an additional idea: Variables that hold an address value called a pointer variable or a pointer, defined using the '*' operator. For example,

    	int *var1;   //defines a pointer variable that holds an address pointing to an integer.
    	char *name;  //defines a pointer variable that holds an address pointing to a character.
    

    The value that the variable holds can be accessed using the '*' operator before the variable name. This operation is sometimes called "using the pointer". To summarize the above concepts, run the code given below and check its output. Paste the output below.

    #include <iostream>
    using namespace std;
    
    int main()
    {
    	float f; 
    	float *f1; //declares a pointer variable 'f1' pointing to a float
    	f1=&f; //must set the first pointer to point somewhere before you use it
    	*f1 = 0.09; //assigns a value to the variable
    
    	cout << "The address of f1 is " << f1 << endl; //prints the address of f1.
    	cout << "The value that f1 points to is " << *f1 << endl; //prints the value that f1 points to.
    	cout << "The value of f is " << f << endl; //prints the value of f.
    
    	char ch; //declare a 'char' variable
    	char *ch1 = &ch; //declare a pointer to the variable
    	*ch1='Y'; //assign ch a value
    
    	cout << "The address of ch1 is " << ch1 << endl; //prints the address of ch.
    	cout << "The value that ch1 points to " << *ch1 << endl; //prints the value that ch points to.
    	cout << "The value of ch is " << ch << endl; //prints the value that ch points to.
    
    	return 0;
    }
    
    
    
    

    Paste the output of running this program here:

  3. Dynamic Allocation of Arrays
  4. Declaring an array with a fixed size like

    	int array[1000];
    has two typical problems:

    * Exceeding maximum. Choosing a real maximum is often impossible because the programmer has no control over the size of the data sets the user is interested in. Erroneous assumptions that a maximum will never be exceeded are the source of many programming bugs. Declaring very large arrays can be extremely wasteful of memory, and if there are many such arrays, may prevent the program from running in some systems.

    * No expansion. Using a small size may be more efficient for the typical data set, but prevents the program from running with larger data sets. If array limits are not checked, large data sets will run over the end of an array with disastrous consequences. Fixed size arrays can not expand as needed.

    These problems can be avoided by dynamically allocating an array of the right size, or dynamically expanding a small array to make it larger as needed. Both of these are done by declaring an array as a pointer and using the new operator to allocate memory, and delete to free memory that is no longer needed.

    Specifically, to expand an array, you must:

    1. Create a new array that is twice is the size of the first array
    2. Copy the data from the old array to the new array
    3. Delete the old array

    To create a variable that will point to a dynamically allocated array, declare it as a pointer to the element type. For example,

    	int *array = NULL;
    creates a pointer to an array of integers. We usually initialize this pointer to NULL just to be safe. The above declaration creates a pointer, but does NOT allocate any memory for the array.

    Allocating memory: Allocating an array with 'new'.

    When the desired size of an array is known, allocate memory for it with the 'new' operator and save the address of that memory in the pointer. Pointers may be subscripted just as arrays are.

    	void* operator new[] (std::size_t size) 

    The example below reads in a number and allocates that size array.

    	int *array = NULL; //declare array pointer
    
    	int num; // size needed for array
    	cout << "Enter a number for array size: ";
    	cin >> num;
    
    	array = new int[num]; //allocate num ints and save their address in 'array'
    
    	if (array!=NULL)
    	{
    		// initialize all elements to value of counter
            	for (int i=0; i < num; i++)
                    	array[i]=i;
    	}
    
    	delete [] array; // return space to the system
    	*array = NULL; // important to set the pointer to NULL after the values it points to are deleted
    
    
    

    Deallocating memory: Freeing unused memory with 'delete'.

    When you are finished with dynamically allocated memory, deallocate it with the 'delete' operator. To release the space for an array, use 'delete [] ptr'. To release space in general, use 'delete ptr'.

    Whenever you use 'delete' to deallocate memory, make sure to set the pointer to that memory back to NULL as well.

    After memory has been deallocated, it can be reused by later 'new' requests. Memory that your program did not deallocate will be automatically deallocated when the program terminates. Never deallocate memory that was not dynamically allocated - the results are unpredictable.

  5. How Things Can Go Wrong
    1. First Problem: Delete/change an alias, use the original...
    2. When two pointers both point to the same location, changes to the value or using the 'delete' operator on one of the pointers affects the other. See the following example:

         float *fptr1; // a float pointer
         float *fptr2; // another float pointer
      
         fptr1 = new float; // use 'new' to make a float for the first pointer to point to
         *fptr1 = 7.3; // and give that float some value
         fptr2 = fptr1; // now, make the second pointer point to the same spot
        
         // Print out the values of the floats that the pointers point to
         cout << "The value that fptr1 points to is: " << *fptr1 << endl;
         cout << "The value that fptr2 points to is: " << *fptr2 << endl;
      
         *fptr2 = 3.3; // and now.. change the value of the float..
      
         // Print them out again -- can be confusing... who changed *fptr1?
         cout << "The value that fptr1 points to is: " << *fptr1 << endl;
         cout << "The value that fptr2 points to is: " << *fptr2 << endl;   
      
         // get rid of fptr2
         delete fptr2; // deallocates the memory where fptr2 points
         fptr2 = NULL; // makes fptr2 point nowhere
      
         // we still have fptr1, right? Let's see what's there.
         cout << "The value that fptr1 points to is: " << *fptr1 << endl;
      
         // oops
      
      
      

      In order to fix any potential problems with printing out values from pointers, add something of the following form around any print statements:

         if (ptr != NULL)
      Update the above code segment to avoid printing out from a memory location that has been deallocated, and run it to ensure it's functioning correctly.

      Paste your updated code and its output here:

    3. Second Problem: Forgetting to delete... out of memory!
    4. It is very important to remember to use 'delete' to deallocate any memory that was allocated with 'new' once it is done being used. Consider the following segment of code:

      #include <iostream>
      using namespace std;
      
      void foo()
      {
         int *localarray = NULL;
         localarray = new int[5000];
      }
      
      int main()
      {
         for(int i = 0; i < 1000000; i++)
            foo();
             
         return 0;
      }
      

      As you can see in the above code, main() consists of a single loop that makes one million calls to the function foo(). Each time foo() is called, it dynamically allocates memory for an array of 5000 integers. Try running this code. (It should crash in a brand new way!)

      The reason this program terminates is because it allocates all available memory through successive calls to foo(), and then tries to allocate more. To prevent this type of crash, you must use the 'delete' operator in foo() to deallocate each instance of the array after foo() is done using it (or in this case, not using it). Make the correct addition to the above code so that it doesn't crash and paste your new program below:

  6. Expanding an Array
  7. Based on the above concepts write a small program that allocates space for an array of integers with SIZE=5 and goes in a loop to initialize the array with the values equal to iteration step (i.e., from 0 to 4). Then, try to use the memory allocation commands introduced above to create a new array that is double the size of the original array. Next, copy the values from the original array to the new array. Then, fill in the remaining elements of the new array with the value corresponding to the iteration step of the previous loop (i.e., from 5 to 9).

    Paste your full program here:

    Submit Work

      This lab assignment will be submitted electronically to the TAs once you fill in the fields below and click on the "submit" button. You do NOT need to print a copy of this document to hand in.

      Your UAID number:
      Your website PASSWORD: