Declaring Defining Statically Allocated Arrays

 < Day Day Up > 



Declaring & Defining Statically Allocated Arrays

Statically allocated arrays, like statically allocated variables, exist in stack memory as opposed to dynamically allocated arrays which are located in memory allocated from the application heap. This section will discuss the declaration and initialization of static arrays of both single and multiple dimensions.

Single-Dimensional Arrays

Single-dimensional arrays, like the one shown in figure 8-1, are declared using the following format:

click to expand

The type of the array denotes what kind of objects the array will contain. Arrays can be constructed from the following types:

  • fundamental types such as int, float, char, etc.

  • user-defined structures and classes

  • pointers to fundamental types, user-defined types, and functions

  • pointer to member

  • enumeration types

  • other arrays

This information is used by the compiler to determine the size of each element. The identifier is the name of the array. Name your arrays like you name your variables – something that makes sense! Here are a few examples:

int number_list[3];          //array of 3 integer objects float account_balances[25];  //array of 25 float objects char search_string[200];     //array of 200 char objects double* double_pointers[10]; //array of 10 pointers to double objects

This last example is interesting in that the array of pointers itself is statically allocated, but the double objects each pointer ultimately points to can be dynamically allocated using the new operator you learned about in chapter 7. You will see a more detailed example of this later.

The examples above use integer literals as the constant expression to denote the size of each array. You can also use a named constant as the following example illustrates:

const int ARRAY_SIZE = 25; int integer_array[ARRAY_SIZE]; //array of 25 integer objects

Accessing Array Elements

There are two primary methods you can use to access the objects stored in an array. The first, and most common method, utilizes the array name and the subscript operator [ ]. The second method, pointer arithmetic, treats the array name like a pointer and uses the pointer dereference operator *. Let us look closer at the subscript method.

Subscript Method

The subscript operator is overloaded in C++ meaning it is used both to declare arrays, as shown in the previous section, and to access individual array elements. To access the objects stored in an array, apply the subscript operator to the name of the array, enclosing between the brackets the element number you wish to access minus one. (Remember, the 1st element in an array is the array name plus a zero offset) The following code declares an array of integers and sets each element to a unique integer value using the subscript method:

Listing 8.1: declaring & using integer array

start example
1  int int_array[5]; 2 3  int_array[0] = 1; 4  int_array[1] = 2; 5  int_array[2] = 3; 6  int_array[3] = 4; 7  int_array[4] = 5;
end example

This code declares a five element array of integers named int_array. Lines 3 through 7 sets each integer object to the value specified. The following example prints the contents of int_array to the console:

Listing 8.2: using integer array

start example
1  cout<<int_array[0]<<endl; 2  cout<<int_array[1]<<endl; 3  cout<<int_array[2]<<endl; 4  cout<<int_array[3]<<endl; 5  cout<<int_array[4]<<endl;
end example

The number appearing between the subscript brackets is referred to as the array index or array subscript. It must be an integer type and can be either a literal, as is used above, or a variable. The following code sets the values of int_array using a for loop and then prints the values to the console with another:

Listing 8.3: manipulating array with for statement

start example
1  int int_array[5]; 2 3  for(int i = 0; i<5; i++) 4      int_array[i] = i+1; 5 6  for(int i = 0; i<5; i++) 7      cout<<int_array[i]<<endl;
end example

Pointer Arithmetic Method

The subscripted array name returns the object located at that memory location. The subscripted array name is exactly equivalent to the array name plus the index dereferenced with the * operator. For example, int_array[1] returns the same element as *(int_array + 1). Look at example 8.4.

Listing 8.4: pointer arithmetic

start example
1 int int_array[5]; 2 3 for(int i = 0; i<5; i++) 4     *(int_array + i) = i+1; 5 6 for(int i = 0; i<5; i++) 7     cout<<*(int_array + i)<<endl;
end example

Beware the Uninitialized Array!

It is important not to make any assumptions about the state of each object in a newly declared array. For example, consider for a moment what the value is of each array element in the following example:

int number_list[3]; //what’s the value of each integer element?

If you guessed garbage you’re right. Examine the code in example 8.5 and the result it produces. Keep in mind that running these examples on your computer will yield different garbage values.

Listing 8.5: garbage out

start example
1  int number_list[3]; 2 3  for(int i=0; i<3; i++) 4      cout<<number_list[i]<<endl;
end example

Notice in figure 8-2 the values contained in the first and third elements of the array. The values represent the random state of the memory region in which the array is created. One way to ensure the values of array elements are initialized properly is to combine the definition of each array element with the array’s declaration.


Figure 8-2: Results of Running Example 8.5

Combining Array Definition with Array Declaration

You can define each element of an array at the same moment you declare it by enclosing element initializers in braces following the declaration as is shown in the following examples:

int number_list[3] = {1,2,3};     //array of 3 ints int number_list[] = {1,2,3,4,5};  //array of 5 ints int number_list[10] = {};         //array of 10 ints initialized to 0 int number_list[10] = {3};        //first element 3, the rest 0

Notice in the second example the constant expression between the brackets is missing. Defining an array in this manner results in an array with the same number of elements as there are initializers. In the third example, the ten element array is initialized to all zeros, whereas in the forth example, the first element is initialized to three and the rest of the elements are initialized to zero.

Now observe the behavior of the following code:

Listing .6: good output

start example
1  int number_list[3] = {}; 2 3  for(int i=0; i<3; i++) 4      cout<<number_list[i]<<endl;
end example


Figure 8-3: Results of Running Example 8.6

All the elements of number_list are initialized to zero.

Arrays of Pointers

An array of uninitialized pointers behaves like an array of uninitialized integers when declared in that each pointer will, unless properly initialized, contain a garbage value. Observe the behavior of the code shown in example 8.7.

Listing 8.7: uninitialized pointers

start example
1  int *int_pointers[3]; 2 3  for(int i=0; i<3; i++) 4      cout<<int_pointers[i]<<endl;
end example


Figure 8-4: Results of Running Example 8.7

To initialize the pointers to null you can use NULL in the initializer list as shown in example 8-4 or you can simply use an empty initializer list as was used in example 8.6.

Listing 8.8: good output

start example
1 int *int_pointers[3] = {NULL}; 2 3 for(int i=0; i<3; i++) 4     cout<<int_pointers[i]<<endl;
end example


Figure 8-5: Results of Running Example 8.8

It is always a good idea to set pointers to NULL if they aren’t pointing to anything in particular.

Now that you have an array of pointers you need to create the object the pointers will ultimately point to. Figure 8-6 gives a visual idea of what needs to happen. Integer objects will be dynamically created in heap memory using the new operator and the resulting memory address assigned to an array element.

click to expand
Figure 8-6: Array of Integer Pointers and Dynamically Created Integer Objects in Heap Memory

The following code declares the array of integer pointers initialized to NULL. It then creates three integer objects and assigns the address of each to an array element:

Listing 8.9: pointer NULL initialization

start example
1  int *int_pointers[3] = {NULL}; 2 3  for(int i = 0; i<3; i++) 4      int_pointers[i] = new int(i+1);
end example

Note the use of the new operator to create the integer objects. The value of each integer is created by calling its constructor using the value of the for loop index value plus one.

The following code prints the values of the integer objects created above:

Listing 8.10: using delete operator on pointer array elements

start example
1  for(int i = 0; i<3; i++) 2      cout<<*int_pointers[i]<<endl; 3 4  for(int i = 0; i<3; i++) 5      delete int_pointers[i]; //use delete to release memory
end example

Notice how the pointer dereference operator * is used with the array to access the actual object pointed to by each array element. Just as with ordinary pointers, you need to ensure you release the memory reserved on the heap back to the operating system using the delete operator.

Multi-Dimensional Arrays

An array of more than one dimension is actually an array of arrays. This section will discuss two-dimensional arrays followed by arrays of three and more dimensions.

Arrays of Two Dimensions

The hardest part of working with multi-dimensional arrays is deciding exactly what each dimension represents, picturing the array representation in your mind, and then translating that understanding into an array declaration. Let us pause for a moment to consider once again the single-dimensional array. Figure 8-7 offers a visual representation of a single-dimensional array of 5 integer objects.

click to expand
Figure 8-7: Single-Dimensional Array Representation and Declaration

You can safely think of a single-dimensional array as having one row by default and that the declaration’s purpose is to establish how many elements, or columns, the row will contain. Thinking in this manner will help you visualize not only single-dimensional arrays, but arrays of two, three or more dimensions.

The declaration of a two-dimensional array looks similar to that of a single-dimensional array with the addition of another set of brackets indicating the number of rows the array will contain. Check out the following examples:

int integer_grid[5][5]; // a 5 x 5 integer array float float_matrix[10][10]; //a 10 x 10 float array

But which number in the above declarations represents the columns and which one represents the rows? C++ stores arrays in row-major order meaning in memory, a row of elements exists, followed by the next row with its elements, and so on for each row. Named constants can be used in place of the integer literals for the constant expression and this will clarify things a little as shown in the following example:

const int ROWS = 5; const int COLS = 5; int integer_grid[ROWS][COLS]; 

Notice how ROWS comes before COLS. There is a pattern here. If you think of array dimensions as groupings or sets you will see that the smallest set of objects, which is ultimately the object itself, always moves to the right in the array declaration. Since, in a single-dimensional array, the array itself can be thought of as a grouping of elements in one row, a two-dimensional array can be thought of as a grouping of rows that each contain a grouping of elements. In other words, an array of arrays. Let us take a look at a visual representation of a two-dimensional array.

click to expand
Figure 8-8: Two-Dimensional Array and Declaration

Figure 8-9 shows how the two-dimensional array depicted above might be represented in memory.

click to expand
Figure 8-9: Two-Dimension Array Memory Representation

Figure 8-9 shows how C++ arranges two-dimensional arrays in memory in row-major order. Row-major order is an arrangement where a row and all its elements are stored followed by the next row and all its elements and so forth for the entire array.

Arrays of Three or More Dimensions

Keeping in mind that in C++ multi-dimensional arrays are simply arrays of arrays helps tame the complexity when thinking of arrays of three or more dimensions. The following code declares a three-dimensional array of integers:

int integer_cube[5][5][5];

Using integer constants to name each dimension results in a clarification of the intended use of each dimension:

const int DEPTH = 5; const int HEIGHT = 5; const int WIDTH = 5; int integer_cube[DEPTH][HEIGHT][WIDTH];

Another programmer might choose different constant names to reflect a different intended use for the array:

const int SHEETS= 5; const int ROWS= 5; const int COLUMNS= 5; int integer_cube[SHEETS][ROWS][COLUMNS];

Figure 8-10 shows one possible visual representation of a three-dimensional array.

click to expand
Figure 8-10: Visual Representation of a Three Dimensional Array

Just as with two-dimensional arrays, the larger dimensional grouping displaces the smaller groupings to the right, so the [SHEETS] dimension is declared before [ROWS], which is declared before [COLUMNS]. A drawing of the memory representation of a three-dimensional array is left as a skill building exercise.

Although most of your programming needs can be satisfied using either one-, two-, or three-dimensional arrays, sometimes arrays with more than three dimensions are required. The following code declares an array of four dimensions:

const int BOOKS = 2; const int SHEETS = 5; const int ROWS = 5; const int COLUMNS = 5; int four_d_int_array[BOOKS][SHEETS][ROWS][COLUMNS];

Figure 8-11 offers a visual representation of this array.

click to expand
Figure 8-11: Visual Representation of a Four-Dimensional Array

Automatic Initialization of Multi-Dimensional Arrays

Multi-dimensional arrays can be initialized at the point of declaration like their single-dimensional counterparts. Example 8.11 demonstrates the declaration and initialization of a three-dimensional array:

Listing 8.11: using 3-dimensional array

start example
1 const int SHEETS   = 5;  2 const int ROWS     = 5;  3 const int COLUMNS  = 5;  4  5 int three_d_int_array[SHEETS][ROWS][COLUMNS] = {};  6  7 for(int i = 0; i<SHEETS; i++){  8    for(int j = 0; j<ROWS; j++){  9       for(int k = 0; k< COLUMNS; k++){ 10          cout<<three_d_int_array[i][j][k]; 11             } 12          cout<<endl; 13       } 14       cout<<endl; 15    }
end example

Figure 8-12 shows the results of running example 8.11.


Figure 8-12: three_d_int_array Initialized to Zeros

Using different combinations of braces and integer values results in different possible array initializations. Consider the code shown in example 8.12.

Listing 8.12: initializing 3-dimensional array

start example
1  int three_d_int_array[SHEETS][ROWS][COLUMNS] = {{{1,2,3,4,5}, 2                                                   {1,2,3,4,5}, 3                                                   {1,2,3,4,5}, 4                                                   {1,2,3,4,5}, 5                                                   {1,2,3,4,5}}};
end example

This results in all the rows of the first sheet being initialized as shown in figure 8-13.


Figure 8-13: All Rows of First Sheet Initialized

Example 8.13 declares the array and initializes the first row of each sheet. Examine the use of the braces in the declaration and see if you can spot a pattern.

Listing 8.13: brace usage

start example
1  int three_d_int_array[SHEETS][ROWS][COLUMNS] = {{{1,2,3,4,5}}, 2                                                  {{1,2,3,4,5}}, 3                                                  {{1,2,3,4,5}}, 4                                                  {{1,2,3,4,5}}, 5                                                  {{1,2,3,4,5}}};
end example

Figure 8-14 shows the results of initializing the array in this fashion.


Figure 8-14: First Row of Each Sheet Initialized

The outermost brace pair represents the array. In the case of three_d_int_array it is an array of five user-conceptualized elements called SHEETS. Each sheet is an array of five user-conceptualized elements called ROWS, and each row is comprised of five integer elements conceptualized as COLUMNS. Figure 8-15 illustrates the relationship between the braces in the array declaration and each of these conceptualizations.

click to expand
Figure 8-15: Relationship of Declaration Braces to Array Elements for three_d_int_array

Using the brace map shown in figure 8-15 as a guide I’ll show you one more initialization scenario. Let us initialize the first three rows of three_d_int_array with the values 1,2,3,0,0, and the rest of the rows to all zeros.

click to expand

Listing 8.14: braceusage

start example
1 int three_d_int_array[SHEETS][ROWS][COLUMNS] = {{{1,2,3}, {1,2,3}, {1,2,3} }, 2                                                  {{1,2,3}, {1,2,3}, {1,2,3} }, 3                                                  {{1,2,3}, {1,2,3}, {1,2,3} }, 4                                                  {{1,2,3}, {1,2,3}, {1,2,3} }, 5                                                  {{1,2,3}, {1,2,3}, {1,2,3} }};
end example

Figure 8-16 shows the results of the initialization shown in example 8.14 above.

click to expand
Figure 8-16: Results of Initialization Shown In Example 8.14



 < Day Day Up > 



C++ for Artists. The Art, Philosophy, and Science of Object-Oriented Programming
C++ For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504028
EAN: 2147483647
Year: 2003
Pages: 340
Authors: Rick Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net