Array
An array is an aggregate data type that lets us access many variables of the same type through a single identifier.
// allocate 30 integer variables in a fixed array
int testScore[30];
Array elements and subscripting
Each of the variables in an array is called an element. Element do not have their own unique names. Instead, to access individual elements of an array, we use the array name, along with the subscript operator([]), and a parameter called a subscript(or index) that tells the compiler which element we want. The process is called subscripting or indexing the array.
In the example above, the first element in our array is testScore[0]
, the second is testScore[1]
, the last is testScore[29]
For an array of length N, the array elements ara numbered 0 through N-1, this is called the array's range
Array data types
Arrays can be made from any data type:
int intArray[3];
double doubleArray[3];
...
Arrays can also be made from structs:
struct Rectangle {
int length;
int width;
}
Rectangle rects[5];
Fixed array declarations
When declaring a fixed array, the length of the array must be a compile-time constant. This is because the length of a fixed array must be known at compile time.
Because fixed arrays have memory allocated at compile time, that introduces two limitations:
- Fixed arrays cannot have a length based on either user input or some other value calculated at runtime
- Fixed arrays have a fixed length that can not be changed
Initializing fixed arrays
initializer list:
int prime[5] = { 2, 3, 5, 7, 11 };
If there are more initializers in the list than the array can hold, the compiler will generate an error.
However, if there are less initializers in the list than the array can hold, the remaining elements are initialized to 0(or whatever value 0 converts to fro a non-integer fundamental type -- e.g. 0.0 for double).This is called zero initialization.
Consequently, to initialize all the elements of an array to 0, you can do this:
int array[5] = { };
In C++11, the uniform initialization syntax can be used instead:
int prime[5] { 2. 3. 5. 7, 11 };
Omitted length
If you are initializing a fixed array of elements using an initializer list
int prime[] = { 2, 3, 5, 7, 11 };
Passing arrays to functions
Because copying large arrays can be very expensive, C++ does not copy an array when an array is passed into a function. Instead, the actual array is passed.
If you want to ensure a function does not modify the array elements passed into it, you can make the array const.
Arrays and loops
Loops are typically used with arrays to do one of three things:
- Calculate a value(e.g. average value, total value)
- Search for a value(e.g. highest value, lowest value)
- Reorganize the array(e.g. ascending order, descending order)
When calculating a value, a variable is typically used to hold an intermediate result that is used to calculate the final value
When searching for a value, a variable is typically used to hold the best candidate value seen so far(or the array index of the best candidate).
Sorting an array is a bit more tricky, as it typically involves nested loops.
Arrays and off-by-one errors
One of the trcikiest parts of using loops with arrays is making sure the loop iterates the proper number of times. Off-by-one errors are easy to make, and trying to access an element that is larger than the length of the array can have dire consequences.
Sorting an array
Sorting is generally performed by repeatedly comparing pairs of array elements, and swapping them if they meet some predefined criteria.
#include
// for C++11
#include
int x = 2;
int y = 4;
std::swap(x, y);
Selection sort
Selection sort performs the following steps to sort an array from smallest to largest:
- Starting at array index 0, search the entire array to find the smallest value
- Swap the smallest value found in the array with the value at index 0
- Repeat steps 1 & 2 starting from the next index
We're going to find the smallest element in the array, and swap it into the first position. Then we're going to find the next smallest element, and swap it into the second position. This process will be repeated until we run out of elements.
for (int i = 0; i < length; ++i) {
int smallest = i;
for (int j = i + 1; j < length; ++j) {
if (scores[j] < scores[smallest]) {
smallest = j;
}
}
std::swap(scores[i], scores[smallest]);
}
Multidimensional Arrays
The elements of an array can be of any data type, including arrays.
An array of arrays is called a multidimensional array.
int array[3][4];
C-style stings
Modern C++ supports two different types of strings:
-
std::string
(as part of the standard library) - C-style strings(natively, as inherited from the C language)
It turns out that std::string
is implemented using C-style strings.
A C-style string is simply an array of characters that uses a null terminator.
A null terminator is a special character('\0', ascii code 0) used to indicate the end of the string. More generically, a C-style string is called a null-terminated string.
To define a C-style string, simply declare a char array and initialize it with a string literal:
char cstring[] = "cstring";
Although "cstring"
only has 7 letters, C++ automatically adds a null terminator to the end of the string for us(we don't need to include it ourselves). Consequently, char array cstring is actually an array of length 8.
#include
int main()
{
char cstring = "cstring";
// length is 8
int length = sizeof(cstring) / sizeof(cstring[0]);
Note: When printing a C-style string, std::cout prints characters until it encounters the null terminator.
It's fine if the array is larger than the string it contains:
// only use 8 characters(4 letters and null terminator)
char cstring[30] = "Lincoln";
C-style strings and std::cin
#include
int main()
{
char name[255]; // declare array large enough to hold 255 characters
std::cout << "Enter your name: ";
std::cin.getline(name, 255);
std::cout << "You entered: " << name << '\n';
return 0;
}
This call to cin.getline()
will read up to 254 characters into char array name(leaving room for the null terminator). Any excess characters will be discarded.
In this way, we guarantee that we will not overflow the array.
Manipulating C-style strings
strcpy()
#include
int main()
{
char source[] = "Copy this!";
char dest[50];
strcpy(dest, source);
std::cout << dest; // prints "Copy this!"
return 0;
}
strcpy()
can easily cause array overflows if you're not careful.
In C++11, strcpy()
was deprecated in favor of strcpy_s()
, which adds a newe parameter to define the size of the destination. However, not all compiler support this function, and to use it, you have to define __STDC_WANT_LIB_EXT1__
with integer value 1.
#define __STDC_WANT_LIB_EXT1__ 1
#include // for strcpy_s
int main()
{
char source[] = "Copy this!";
char dest[5]; // note that the length of dest is only 5 chars!
strcpy_s(dest, 5, source); // An runtime error will occur in debug mode
std::cout << dest;
return 0;
}
strlen()
Note: the difference between
strlen()
andsizeof()
.strlen()
prints the number of characters before the null terminator, whereassizeof()
returns the size of the entire array, regardless of what's in it.
strcat()
Appends one string to another(dangerous)
strncat()
Appends one string to another(with buffer length check)
strcmp()
Compare two strings(return 0 if equal)
strncmp()
Compare two strings up to a specific number of characters(return 0 if equal)
Avoid using C-style strings altogether whenever possible.
Usestd::string
instead, it's easier, safer, and more flexible.