C++ Core Guidelines: Bounds Safety


Today's post is about the second profile of the C++ Core Guidelines: Bounds Safety. The goal of the profile bounds safety is it that you operate inside the bounds of allocated memory.



The profile names the two enemies for bounds safety: pointer arithmetic and array indexing. Additionally, when you use a pointer, it should only address a single object but not an array. To make the profile bounds safety complete, you should combine it with the rules to type safety and lifetime safety. Type safety was the topic of my two previous posts: C++ Core Guidelines: Type Safety and C++ Core Guidelines: Type Safety by Design. Lifetime safety will be the topic of my next post.

Bounds Safety

Bounds safety consists of four rules:

  • Bounds.1: Don’t use pointer arithmetic
  • Bounds.2: Only index into arrays using constant expressions
  • Bounds.3: No array-to-pointer decay
  • Bounds.4: Don’t use standard-library functions and types that are not bounds-checked

The four rules to bounds safety mention three rules of the C++ core guidelines. As in the last posts to the profiles, I will make my additions if necessary.

Bounds.1: Don’t use pointer arithmetic, Bounds.2: Only index into arrays using constant expressions, and Bounds.3: No array-to-pointer decay

The reason for the three rules boils down to the three do's: pass pointers to single objects (only), keep pointer arithmetic simple, and use std::span. The first do can also be formulated negatively: don't pass pointers to arrays. I assume you don't know std::span. std::span<T> represents a non-owning range of contiguous memory. This range can be an array, a pointer with a size, or a std::vector.

Let me cite the words of the guidelines: "Complicated pointer manipulation is a major source of errors.". Why should we care? Of course, our legacy code is full of functionality, such as this example:

void f(int* p, int count)
    if (count < 2) return;

    int* q = p + 1;    // BAD

    int n = *p++;      // BAD

    if (count < 6) return;

    p[4] = 1;          // BAD

    p[count - 1] = 2;  // BAD

    use(&p[0], 3);     // BAD

int myArray[100];     // (1)

f(myArray, 100),      // (2)


The main issue with this code is that the caller must provide the correct length of the C-array. If not, we get undefined behaviour.

Think about the last lines (1) and (2) for a few seconds. We start with an array and remove its type information by passing it to the function f. This process is called an array to pointer decay and is the reason for a lot of errors. Maybe we had a bad day, and we count the number of elements wrong or the size of C-array changed. Anyway, the result is the same: undefined behaviour. The same argumentation will also hold for a C-string.

What should we do? We should use a suitable data type. C++20 supports std::span. Have a look here:

void f(span<int> a) // BETTER: use span in the function declaration
    if (a.length() < 2) return;

    int n = a[0];      // OK

    span<int> q = a.subspan(1); // OK

    if (a.length() < 6) return;

    a[4] = 1;          // OK

    a[count - 1] = 2;  // OK

    use(a.data(), 3);  // OK


Fine! std::span checks at run-time its boundaries.

But I hear your complaints: We don't have C++20. No problem. It's quite easy to rewrite the functions f using the container std::array and the method std::array::at. Here we are:

// spanVersusArray.cpp

#include <algorithm>
#include <array>

void use(int*, int){}

void f(std::array<int, 100>& a){

    if (a.size() < 2) return;

    int n = a.at(0);      

    std::array<int, 99> q;
    std::copy(a.begin() + 1, a.end(), q.begin());      // (1)

    if (a.size() < 6) return;

    a.at(4) = 1;          

    a.at(a.size() - 1) = 2;

    use(a.data(), 3); 

int main(){

    std::array<int, 100> arr{};



The std::array::at operator will check at runtime its bounds. If pos >= size(), you will get an std::out_of_range exception. If you look carefully at the spanVersusArray.cpp program, you will notice two issues. First, the expression (1) is more verbose than the std::span version and second, the size of the std::array is part of the signature of the function f. This is bad. I can only use f with the type std::array<int, 100>.  In this case, the checks of the array size inside the function are superfluous. 

To your rescue, C++ has templates; therefore, it's easy to overcome the type restrictions but staying type-safe.


// at.cpp

#include <algorithm>
#include <array>
#include <deque>
#include <string>
#include <vector>

template <typename T>
void use(T*, int){}

template <typename T>
void f(T& a){

    if (a.size() < 2) return;

    int n = a.at(0);      

    std::array<typename T::value_type , 99> q;                 // (5)
    std::copy(a.begin() + 1, a.end(), q.begin());     

    if (a.size() < 6) return;

    a.at(4) = 1;          

    a.at(a.size() - 1) = 2;

    use(a.data(), 3);                                          // (6)

int main(){

    std::array<int, 100> arr{};                                             
    f(arr);                                                    // (1)
    std::array<double, 20> arr2{};
    f(arr2);                                                   // (2)
    std::vector<double> vec{1, 2, 3, 4, 5, 6, 7, 8, 9};
    f(vec);                                                    // (3)
    std::string myString= "123456789";
    f(myString);                                               // (4)
    // std::deque<int> deq{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    // f(deq);                                                 


Now, the function f works for std::array's of different sizes and types (lines (1) and (2)) but also for an std::vector (3) or an std::string (4). This containers have in common that their data is stored in a contiguous memory block. This will no hold std::deque; therefore, the call a.data() in expression (6) fails. A std::deque is a kind of doubly-linked list of small memory blocks.


The expression T::value_type (5) helps me to get the underlying value type of each container. T is a so-called dependent type because T is a type parameter of the function template f. This is the reason, I have to give the compiler as a hint that T::value_type is a type: typename T::value_type.

Bounds.4: Don’t use standard-library functions and types that are not bounds-checked

I have already written a post C++ Core Guidelines: Avoid Bounds Errors. This post gives background information to this rule and provides do's. 

What's next?

The name of the third profile is Lifetime Safety Profile. This profile which is the topic of my next post boils down to one rule: Don’t dereference a possibly invalid pointer.


Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Darshan Mody, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, espkk, Wolfgang Gärtner,  Louis St-Amour, Stephan Roslen, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Avi Kohn, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Neil Wang, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, and Peter Ware.


Thanks in particular to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, and Embarcadero Marketing.



I'm happy to give online-seminars or face-to-face seminars world-wide. Please call me if you have any questions.

Bookable (Online)


Standard Seminars 

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.


Contact Me

Modernes C++,


My Newest E-Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code


Today 39723

Yesterday 8369

Week 56776

Month 223610

All 5520714

Currently are 197 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments