Following up on last week’s article on std::vector
, today we will be focusing on std::array
. Where std::vector
represented dynamically sized arrays, std::array
is a container that represents arrays of fixed size. std::array
lends itself nicely to use in embedded systems, as memory can be statically allocated at compile-time.
std::array
Overview
In order to utilize std::array
, you will need to include the array
header:
#include <array>
std::array
is a header-only implementation, which means that once you have a C++ runtime set up for your target system you will get this feature for free.
std::array
provides many benefits over built-in arrays, such as preventing automatic decay into a pointer, maintaining the array size, providing bounds checking, and allowing the use of C++ container operations.
As mentioned above, std::array
is a templated class that represents fixed-size arrays. The size of a std::array
is known at compile time, and the underlying buffer is stored within the std::array
object itself. These two facts are very useful for embedded systems programmers, as they can control buffer sizes and storage location during compilation time. Avoiding the need for dynamic memory allocation saves computational cycles and reduces memory fragmentation. If you declare the object on the stack, the array itself will be on the stack. If you put it in a global scope, it will be placed into global static storage.
Unlike std::vector
, which requires 24 bytes of overhead, no overhead is needed for a std::array
.
Creating a std::array
The std::array
container is prototyped on two elements: the type that you want to be stored and the size of the array. std::array
containers of different sizes are viewed as different types by the compiler.
//Declares an array of 10 ints. Size is always required
std::array<int, 10> a1;
//Declare and initialize with an initializer list
std::array<int, 5> a2 = {-1, 1, 3, 2, 0};
You can make new arrays via copy:
//Making a new array via copy
auto a3 = a2;
//This works too:
auto a4(a2);
And you can also copy arrays of the same size by using the =
operator:
//Assign a2 to a3's values:
a2 = a3;
// But you can only use the '=' operator on arrays of equivalent size.
//Error:
//a1 = a2; //<[...],10> vs <[...],5>! invalid
At least you don’t have to worry about remembering memcpy
argument order or writing past the end of your buffer!
Accessing Data
While the size of a std::array
is fixed at compile time, the contents of a std::array
can be modified during runtime. The familiar []
operator can be used to access specific elements:
//Assigning values works as expected
a3[0] = -2;
However, as with std::vector
, the []
operator does not use bounds checking. If you want to access an element with bound checks enabled, use the at()
function.
std::cout << "a2.at(4): " << a2.at(4) << std::endl;
// Bounds checking can generate exceptions. Try:
//auto b = a2.at(10);
You can also access the front()
and back()
member functions to get the members at the beginning & end of the array.
data()
Like std::vector
, std::array
doesn’t implicitly decay into a raw pointer. If you want to use the underlying std::array
pointer, you must use the data()
member function.
For example, let’s assume you are using an API with a C-style buffer interface:
void carr_func(int * arr, size_t size)
{
std::cout << "carr_func - arr: " << arr << std::endl;
}
If you tried to pass a std::array
for the first argument, you would generate a compiler error.
../../array.cpp:44:2: error: no matching function for call to 'carr_func'
carr_func(a2);
^~~~~~~~~
../../array.cpp:4:6: note: candidate function not viable: no known conversion
from 'std::array<int, 5>' to 'int *' for 1st argument
void carr_func(int * arr)
Instead you need to use the data()
member:
//Error:
//carr_func(a2, a2.size());
//OK:
carr_func(a2.data(), a2.size());
size()
and max_size()
You can access the size of a std::array
using the size()
member function. max_size()
is also valid for std::array
. However, since the size of a std::array
is constant, max_size
will always be equal to size
.
empty()
std::array
has a specific use for the empty()
member function which differs from other containers: it only returns True
if the array size is 0:
std::array<int, 0> a_empty;
The underlying empty()
operation checks if the container has no elements (begin()
== end()
). Since std::array
is statically sized, this condition is only hit when you have a zero-length array.
Container Operations
Since std::array
is a container class and provides the basic container interfaces. Since a std::array
cannot grow or shrink, all functionality related to resizing or remembering a current position has been removed (e.g. push_back
).
However, you can still use a std::array
with functions that are written to operate on container classes, such as std::sort
.
std::sort(a1.begin(), a1.end());
Also worth noting – unlike built-in arrays (which decay into a pointer), a std::array
container can be passed by value into a function.
Putting it All Together
Example code for std::array
can be found in the embedded-resources
Github repository.
Further Reading
- cppreference:
std::array
- Articles/Explanations
- Intro to
std::vector
Using C++ Without the Heap
Want to use C++, but worried about how much it relies on dynamic memory allocations? Our course provides a hands-on approach for learning a diverse set of patterns, tools, and techniques for writing C++ code that never uses the heap.
Learn More on the Course Page
Link to https://embeddedartistry.com/blog/2017/6/28/2017/6/20/an-introduction-to-stdvector is broken.
Thanks, I have fixed the links
How do I use it in a template?
The coderwall explains in Some detail the theory of how best to get the size of an array in C++ https://coderwall.com/p/nb9ngq/better-getting-array-size-in-c
Is it wise to consider the operator [] access to std::array negligble and the same as a c-style array element access?
You can see from this example
https://godbolt.org/z/GTf3a54sh
that for a constant array, accessing the c_style array is resolved at compile time in comparison to std::array which requires to call the operator []