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 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: The 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 a std::vector (3) or a std::string (4). These 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, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, espkk, Louis St-Amour, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Neil Wang, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Tobi Heideman, Daniel Hufschläger, Red Trip, Alexander Schwarz, Tornike Porchxidze, Alessandro Pezzato, Evangelos Denaxas, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Dimitrov Tsvetomir, Leo Goodstadt, Eduardo Velasquez, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, and Michael Young.


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



My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small



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

Bookable (Online)


Standard Seminars (English/German)

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

Interactive Course: The All-in-One Guide to C++20

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code


Today 4815

Yesterday 8162

Week 21685

Month 139887

All 7407727

Currently are 172 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments