Name: _____________________ Class: CET 375
SSN/ID:   _____________________ Section & Group: ____________
Arrays and Addresses


The C++ programming language is based on the C programming language and contains all of the features provided in C. Some of these features have been replaced by modern counterparts that are more consistent with the object-oriented approach to programming. There are certain aspects of C, however, that are frequently omitted from first programming courses but with which C++ programmers should be familiar (because many of them will be employed as C programmers or at least expected to be familiar with C). This is especially true of data structures. The data structures provided in C, although few in number, are usually implemented very efficiently. Also, they and other C features are often used to implement the new C++ data types.

One of these important data structures is the array, and the objective of this lab exercise is to review C-style arrays. We will look first at one-dimensional arrays, at how they are declared and processed. Multi-dimensional arrays are considered at the end of the lab exercise and in Project 5B. Additional information about arrays can be found in Section 2.3 of the text C++: An Introduction to Data Structures.

In general, please see this site, http://www.swcp.com/~dodrill/controlled/extras/arrays.html, for an excellent introduction to arrays and their relationships to pointers. In particular, the name of an array is a constant pointer and is different from a regular pointer. E.g., suppose you have an array named array_name.

In C, array_name really does not refer to the whole array but rather the value of the name is a pointer constant that is the address of the first element. The type of this pointer constant is the type of the array elements, element_type. If the type of the elements is int, then the type of array_name is "constant pointer to int."

The last paragraph does not mean that arrays and pointers are the same. One exhibit in this section shows that an array has very different characteristics from a pointer. The most obvious difference is the result of applying the sizeof() operator to an array and to a pointer; an array is not a scalar, but a pointer is. When an array name appears in an expression, the compiler generates a pointer constant. A compiler uses the pointer constant since automatic and static arrays are fixed in memory at compile-time. In other words, such "pointers" aren't stored; they are just used internally by the compiler.

When it comes to arrays, pointer substitution does not occur in two places:

  1. The address of an array, &array_name, is the address of the first element, not the address of some pointer constant.
  2. Applying sizeof(array_name) yields the size of the entire array, not the size of a pointer to an array.
In all other situations, the pointer substitution occurs, including assignment. When we attempt to assign one array to another, we find that we are not copying the contents of one array so that the values become the contents of the target array; what we assign is the copy of a pointer. Consequently, if the target in an assignment is an array, we get a compiler error: the array name is a pointer constant. In C, to assign one array to another, we must perform a loop that copies individual array elements (no so in C++).

A. One-dimensional arrays

  1. To begin, start typing the source code for a new program array.cpp (by typing emacs array.cpp &) that contains only the following:
        #include <iostream>
        using namespace std;
    
        int main() {
        }
        
  2. Add typedef statements of the from:

    typedef array-element-type type-name[array-size];

    ahead of main() to define two data types:

    IntegerArray for arrays with 8 integer elements
    CharArray for arrays with 10 character elements
    For example, using something like typedef int IntegerArray[ISIZE];
  3. Declare and initialize an IntegerArray variable prime to be an array containing the 8 integers 2,3,5,7,11,13,17, and 19 using a declaration of the form:

    type-name array-name = {list-of-values};

    For example, using something like IntegerArray prime = {0,0,0};

  4. Check that the array has been initialized by writing a for loop to display the elements of prime. Compile and execute your program to check if your statements are correct.
  5. a. It is an error to give too many values in the initializer list of an array. However, some compilers do not "bomb" the program when this happens.
    Question: What happens with your compiler if there are surplus values in the initializer list of an array? Find out by changing the initialization of prime to use more than 8 integers in the initialization and outputting the array elements. Describe what happens.





    b. It is not an error to give too few values in the initializer list of an array.
    Question: What happens in this case? Find out by changing the initialization of prime to use fewer than 8 integers in the initialization and outputting the array elements. Describe what happens.





  6. Now we want to repeat #5 but with character arrays.
    First, comment out the declaration of the integer array prime and the for loop that displays its elements — that is, leave them in your program but add // at the beginning of the line(s) or enclose them between /* and */. Now declare the CharArray variable animal initialized with characters 'r','h','i','n','o','c','e','r','o', and 's'.
    a. Check that the array has been initialized by adding a for loop to display the elements of animal, one character at a time, and all on the same line; also, print something like *** after these characters (on the same line). What's the output?




    b. What if too many initialization values are used? Describe what happens if you use more than 10 values.




    c. What if too few initialization values are used? Use the first 5 characters. Describe the output produced.




    d. It probably isn't clear what happened with the uninitialized character array locations. To see what did happen, modify the output statement in your loop to display the ASCII code of each character. (How? Use a typecast like int (animal[i]) to convert chars to ints.) What value is stored in uninitialized array elements?




  7. A character array may also be initialized using a string instead of a list of separate character constants. Character arrays may also be output using cout << char-array << ....
    a. Modify your declaration of animal to initialize it with "elephant" and replace the for loop used to display the individual characters of array animal with a simple output statement:
    	cout << animal << "***\n";
    	
    Execute the program and describe what happens; i.e., what is output:




    b. Repeat a, but use "rhinoceros" as the initialization string.




    c. You may get an error message even though the array has been declared to have 10 elements.
    Character arrays like animal that are used to store strings should be declared large enough to store one extra character at the end of each string value, namely, the null character '\0' whose ASCII code is 0. This character is used by functions that process strings stored in character arrays to mark the end of the string.
    1. Change the initialization string of animal to "zebra".
    2. Now write C++ statements below that could be used to determine the length of the string stored in animal; that is, the number of non-null characters. Test that your code works by entering it into your program and executing it.
    Note: For fun(?): See if you can do this using a for loop with an empty body (think of using cout << j++ in the loop header).




    d. The end-of-string mark (i.e., the null character '\0') gets placed at the end of each initialization string or a string that is input for a character array (e.g., cin >> animal;) provided that the character array has space for it. If it doesn't get stored, one cannot expect string operations and functions to work correctly.




B. Arrays and addresses

    1. First, do the following:
      1. Remove or comment out the declarations and statements that use the character array animal.
      2. Add declarations of three IntegerArray variables in the following order:
        first initialized to all 0s.
        arr initialized to all 1s.
        last initialized to all 2s.
    2. Now, add output statements to display the addresses of first, arr, and last. In the space below, draw a memory map for these 3 array variables.

      Reminder: To get hex addresses displayed, you may have to cast them to void * (to display the address in Hexadecimal):
      	    cout << (void *) &variable << ...
      	    
      or cast them to unsigned (to display the address in Decimal):
      	    cout << (unsigned) &variable << ...
      	    

      Your memory map might look something like this:
      Variable Name arr[0] OR
      *(arr)
      arr[1] OR
      *(arr + 1)
      arr[2] OR
      *(arr + 2)
      Address \x...50 \x...54 \x...58
      Value 1 1 1






      How many bytes of memory were allocated to each array? ____________________
      Use the sizeof operator to verify your answers.

      What is the address of arr[2]? ____________________
      Of arr[3]? ____________________





  1. Now use the array variables first, arr, and last instead of &first, &arr, and &last in your output statement that displays the addresses of the arrays. Describe how the output changes (if it does).




  2. Now, add statements to display &arr[0], &arr[1], ..., &arr[7]
    and, add statements to display   arr,     arr+1, ...,   arr+7.

    You may use for loops if you wish; that is, display &arr[i] and arr+i and vari i from 0 to 7. What output is produced?




    This example should show that an array name like arr is the address of its first element arr[0], arr+1 is the address of arr[1], and, in general, arr+i is the address of arr[i]

  3. Now, add statements to display the values of:
        arr[0],arr[1],arr[2],arr[3],arr[4],arr[5],arr[6],arr[7]
        
    and also add statements to display the values of:
        *arr,*(arr+1),*(arr+2),*(arr+3),*(arr+4),*(arr+5),*(arr+6),*(arr+7)
        
    (You may use for loops.) What output is produced?




    What happens if the parentheses in the *(arr+i) expressions are removed? Explain why this happens. (Hint: Check the priorities of operators * and +.)




    This example shows that the base-address + offset notation *(arr + i) is an alternative to an array reference arr[i]. As a general rule, however, the usual array reference notation (with the subscript operator []) should be used because it is clearer and thus easier to understand.

C. Out-of-range indices

  1. Question: What happens if array indices get out of range?

    Find out by adding the following assignment statements before the statements in #11 used to display the elements of the three arrays:

        arr[-2] = -99;
        arr[9]  = 99;
        
    This overwrites the values stored at the memory addresses (arr - 2) and (arr + 9) (which were being used by the arrays first and last). Now run the program again. List the elements of the three arrays:
        first:  ____________________________________________________
        
        arr:    ____________________________________________________
        
        last:   ____________________________________________________
        
    Now, display the values of all the array elements arr[-8],arr[-7],...,arr[15] and explain why we got the output we did for the elements of first and last.




D. Multi-dimensional arrays

A two-dimensional array like:

       _           _
      | 11 22 33 44 |
mat = | 55 66 77 88 |
      | -1 -2 -3 -4 |
      ‾‾           ‾‾
having 3 rows and 4 columns (a 3x4 matrix) can be declared and iitialized by:
int mat[3][4] = {{11,22,33,44},
                 {55,66,77,88},
                 {-1,-2,-3,-4}};
(It could all be on one line.) mat[i][j] is the element in the i-th row and j-th column of mat, numbering from 0. For example, mat[1][3] is 88.

In addition, you can allocate this array dynamically using pointers. For example, to declare a dynamic two-dimensional array A, you would do:

int **A;

A = new (int *) [rows];
for (int i = 0; i < rows; i++) {
   A[i] = new int [cols];
}

for (int i = 0; i < rows; i++) {
   delete [] A[i];
}
delete [] A;
This would like this in memory:

The elements of mat can be displayed by nested for loops like:

for (int i=0; i <= 2; i++) {
   for (int j=0; j <= 3; j++) {
      cout << setw(5) << mat[i][j];
   cout << endl;
}
You might need to add #include <iomanip> in order to use the input/output manipulation codes.

In addition, you can compute the offset to an element explicitly using:

mat[cols*i + j] == mat[i][j] 
Experiment with declarations and output statements like these and display some addresses to answer the following questions:

Hand In: This lab handout with the answers filled in and a listing of the program developed while working through the lab exercise (use the enscript command from your Programming Style Sheet to print it out:
enscript -E -G -2rj -M Letter -PECT2_PS <filename>. Or, you can use the a2ps command: a2ps <filename>).


Ricky J. Sethi <rickys at sethi.org>
Last modified: Mon Mar 28 18:33:51 PST 2005