Whenever a program creates a new array, automatically or dynamically, the elements initially contain unspecified values. Memory can never be empty, but until the program explicitly stores a value in an array element, we say the value is "unknown," "unspecified," or "undefined;" colloquially, we say it contains "garbage." Often, the program initializes the elements one at a time with user or file input. However, programmers can initialize the array elements efficiently and compactly with an initializer list when they know the initial values at compile time. Figure 1 illustrates the initialization syntax and its most recent evolution.
int test[5] = { 0, 1, 2, 3, 4 }; |
int* test = new int[5]; |
(a) | (b) |
int test[5]{ 0, 1, 2, 3, 4 }; |
int* test = new int[5]{ 0, 1, 2, 3, 4 }; |
(c) | (d) |
new
.The number of values in the initializer list may never exceed the number of elements in the array. But it's okay if the array is larger than the number of initializer values. When more elements are in the array than values in the initializer list, the computer automatically initializes the remaining or unmatched elements to zero. This behavior suggests a compact notation or "trick" for initializing all array element values to zero.
int test[5] = {}; or int test[5]{}; |
int* test = new int[5]{}; |
(a) | (b) |
Specifying the size of an array but using an empty initializer list is only one useful "trick." Another notation allows us to omit the size of the array but to specify the size implicitly with the number of elements in the initializer list. This notation, more limited in use than the element zeroing notation, is only useful when programmers know the element values in advance - for example when an array supports a repetitive computation.
int month_length[12] =
{
31, 28, 31, 30,
31, 30, 31, 31,
30, 31, 30, 31
};
|
int month_length[] =
{
31, 28, 31, 30,
31, 30, 31, 31,
30, 31, 30, 31
};
|
(a) | (b) |
[12]
. Next, it implies the size with the number of elements in the initializer list.We can extend the notation of Figure 3 to two-dimensional arrays.
int test_scores[5][4] =
{
95, 98, 97, 96,
79, 89, 79, 85,
99, 98, 99, 99,
90, 89, 83, 86,
75, 72, 79, 69
};
|
int test_scores[][4] =
{
95, 98, 97, 96,
79, 89, 79, 85,
99, 98, 99, 99,
90, 89, 83, 86,
75, 72, 79, 69
};
|
(a) | (b) |
test_scores[0][0] = 95 test_scores[0][1] = 98 . . . . test_scores[1][0] = 79 . . . .
It's possible to extend the notation of Figure 4 to higher dimensions but doing so results in code that is hard to read, understand, and maintain. Fortunately, higher-dimensioned arrays are less common in most problem domains, and the need to initialize multidimensional arrays is even less common. One last bit of notation, also rare, is left to explore.
The following notation allows us to write code that counts the number of elements in an array. Its usefulness is generally limited to exceptional cases where the program has a large array of unchanging data similar to (but usually larger than) the month_length array above. Even then, the technique is most useful if the number of elements, and therefore the size of the array, is also subject to change.
int month_length[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int number = sizeof(month_length)/sizeof(int); |
T data[n]; |
(a) | (b) |
sizeof
returns the total number of bytes allocated to store it. When the operand is a data type, the returned value is the number of bytes needed to store an instance of that type in memory.
sizeof
operator to calculate the number of elements in an array.sizeof(data) / sizeof(T) = n × S/S = n
.
Understanding what takes place when we use the sizeof
operator will make it easier for us to appreciate when we can use it and when using it is not appropriate.
T data[n]; |
void function(T* p) {...}or T* p = new T[n]; |
(a) | (b) |
sizeof
. The illustration continues to use the pseudo-code and the general data type T introduced in the previous figure.
sizeof(data)
is the total number of bytes allocated to store the array: n × sizeof(T)sizeof
returns the size of the pointer variable, not the data to which it points. So, sizeof(p)
is the size of the pointer variable, that is, the number of bytes required to store an address (usually 4 or 8 bytes), which is independent of the actual size of the array.