Modern C++:Efficient and Scalable Application Development
上QQ阅读APP看书,第一时间看更新

Using Arrays

As the name suggests, a C++ built-in array is zero or more items of data of the same type. In C++, square brackets are used to declare arrays and to access array elements:

    int squares[4]; 
for (int i = 0; i < 4; ++i)
{
squares[i] = i * i;
}

The squares variable is an array of integers. The first line allocates enough memory for four integers and then the for loop initializes the memory with the first four squares. The memory allocated by the compiler from the stack is contiguous and the items in the array are sequential, so the memory location of squares[3] is sizeof(int) following on from squares[2]. Since the array is created on the stack, the size of the array is an instruction to the compiler; this is not dynamic allocation, so the size has to be a constant.

There is a potential problem here: the size of the array is mentioned twice, once in the declaration and then again in the for loop. If you use two different values, then you may initialize too few items, or you could potentially access memory outside the array. The ranged for syntax allows you to get access to each item in the array; the compiler can determine the size of the array and will use this in the ranged for loop. In the following code, there is a deliberate mistake that shows an issue with array sizes:

    int squares[5]; 
for (int i = 0; i < 4; ++i)
{
squares[i] = i * i;
}
for(int i : squares)
{
cout << i << endl;
}

The size of the array and the range of the first for loop do not agree and consequently the last item will not be initialized. The ranged for loop, however, will loop through all five items and so will print out some random value for the value of the last value. What if the same code is used but the squares array is declared to have three items? It depends on the compiler you are using and whether you are compiling a debug build, but clearly you will be writing to memory outside of that allocated to the array.

There are some ways to mitigate these issues. The first one is declaring a constant for the size of the array and use that whenever your code needs to know the array size:

    constexpr int sq_size = 4; 
int squares[sq_size];
for (int i = 0; i < sq_size; ++i)
{
squares[i] = i * i;
}

The array declaration must have a constant for the size, and that is managed by using the sq_size constant variable.

You may also want to calculate the size of an already allocated array. The sizeof operator, when applied to an array, returns the size in bytes of the entire array, so you can determine the size of the array by dividing this value by the size of a single item:

    int squares[4]; 
for (int i = 0; i < sizeof(squares)/sizeof(squares[0]); ++i)
{
squares[i] = i * i;
}

This is safer code, but clearly it is verbose. The C runtime library contains a macro called _countof that performs this calculation.