Accessing an Array Out of Bounds in C++

Accessing an Array Out of Bounds in C++

Have you ever accessed any array index in C++ without verifying its bounds? What if you felt everything was going all right with your program, but there is some hidden memory issue? C++ does not perform bound checking by default which may lead to undefined behavior, crashes, or security risks. In this article, we discuss out-of-bounds access, the associated issues that remain hidden, and how these can be avoided using STL containers, Address Sanitizers, and Valgrind.

Table of Contents:

Accessing an Array

In C++, accessing an array out of bounds leads to undefined behavior(UB), i.e., the program may behave unpredictably, unlike languages like Python that throw an error like IndexError. However, the C++ case is different from Python, as C++ does not perform the checking for automatic bounds for performance reasons.

Example:

Cpp

Output:

Accessing an Array Output

In the above code, the given array consists of only 3 ({10, 20, 30}) elements, but the arr[5] accesses the memory outside the spaces. This could lead to output as unpredictable behavior, or it may print some garbage value and crash.

Why Does Accessing Array Out-of-Bound Does Not Give an Error in C++?

After defining the array and allocating the memory to it, the compiler does not track if the access of the array is present in the bounds or not. C++ does not stop the execution in case you try to access an index that is out of bounds, it simply reads or writes at the memory location that might belong to another variable or object.

What Happens When Accessing Out-of-Bounds?

Depending on the memory location of the variable and object, the out-of-bound leads to:

  • Reading unallocated memory: If the memory is protected, accessing this may return garbage values or may even lead to the crashing of the program.
  • Writing to unallocated memory: This may corrupt the data or introduce security vulnerabilities, e.g., buffer overflow exploits.
  • No intermediate error: It can cause bugs if the accessed memory is still present in the program’s allocated space.

How do Errors Occur?

Due to out-of-bounds, the errors may cause:

  1. Segmentation faults: If you are accessing the memory outside the program’s assigned range, it will give segmentation faults.
  2. Heap corruption: If we write a program beyond the dynamically allocated memory in the heap.
  3. Stack smashing: If we write a program beyond the bounds of a local array.
  4. Silent logical errors: This error occurs when unintended memory modifications occur.

Preventing Out-of-Bounds Access

1. Use STL Containers

To avoid errors like unpredictable behavior, garbage values, or crashing of the program. You may also utilize STL containers such as std::vector or std:: array since they have more secure access methods. In std::vector, the .at(index) function does the bound checking and checks if the index is in range or out of range of the array. In case if it is out of range, it throws an exception.

Example:

Cpp

Output:

Use STL Containers Output

Explanation: In the above code, by using vec.size(), we can check whether the index is in or out of range. In case the index is invalid, the std::out_of_range exception prevents the program from accessing invalid memory. And the catch block handles the exception, instead of causing a crash, it displays an error message.

2. Use Address Sanitizers

When a program writes beyond the dynamically allocated memory like a heap, Heap corruption occurs. This may lead to memory corruption, crashes, or undefined behavior. This mostly happens due to out-of-bounds access or double deletion. Although the heap dynamically stores the allocated data by using new or malloc. Such errors can overwrite critical memory areas, causing serious bugs.

Example:

Cpp

Output:

Use Address Sanitizers Output

Explanation: Here, memory is allocated for 3, but writing to arr[3] accesses the memory beyond the assigned space, and this may cause heap corruption.This can lead to additional crashes or undefined behavior. To prevent these errors, we can always check the array bounds and should use safe alternatives like std::vector or debugging tools like Address sanitizer like fsanitize=address for detecting the memory errors.

3. Bounds Checking in Debug Mode

Bounds checking is used for detecting memory access of the out-of-bounds in arrays or dynamic memory allocations. However, C++ doesn’t perform automatic bound checking for arrays, We can use STL containers like std::vector to provide a safer way to access the elements by using .at(), which throws an exception if an index goes out of range.

Example:

Cpp

Output:

Bounds Checking in Debug Mode Output

Explanation: In the above code, calling std::vector: in .at() performs range checks and throws an exception if the index is out of range. The use of try-catch is to safely handle the error so that the program doesn’t crash and continues running.

4. Using Smart Debugging Tools like Valgrind

Memory-related issues such as out-of-bounds, memory leaks, invalid memory use are hard to track down in C++ and so tools like valgrind are used. Valgrind identifies these types of errors by examining memory usage during program execution. The memcheck tool of Valgrind is used to report memory leaks, uninitialized memory access, and buffer overflows, which makes it easier.

Example:

Cpp

Output:

Using Smart Debugging Tools like Valgrind Output

Explanation: In the above code, by using the new, the memory is allocated, and then by using delete the allocated memory is freed to prevent the leaks. Running Valgrind tools confirms that all memory is correctly managed to ensure the program is safe and error-free.

Note:

Detecting the Issue with Valgrind:

run and compile with Valgrind

g++ -g -o memory_leak memory_leak.cpp  
valgrind --leak-check=full ./memory_leak

Conclusion

In C++, accessing out-of-bounds of an array results in undefined behavior that can lead to crashes, memory leaks, and vulnerabilities. C++ would not do the automatic bound checking for performance reasons, however, STL containers and additional debugging tools like Address Sanitizers and Valgrind for safe memory access in C++ ensure better detection, prevent the issue, and make the programs secure and reliable.

FAQs on Why Accessing an Array out of bounds does not Give Any Error in C++?

1. Why does C++ allow out-of-bounds access?

C++ focuses on performance and does not provide for bound checking automatically.

2. What does accessing an invalid array index do?

It may return a garbage value, may crash, or memory corruption may happen.

3. How do we avoid out-of-bounds access?

By using STL containers like std::vector::at() or checking bound conditions manually.

4. What is Address Sanitizer, and how does it help?

Detecting memory errors like buffer overflow and heap corruption at runtime.

5. How can Valgrind help in debugging?

With Valgrind’s Memcheck tool, find memory leaks, invalid accesses, and using uninitialized memory.

About the Author

Technical Research Analyst - Full Stack Development

Kislay is a Technical Research Analyst and Full Stack Developer with expertise in crafting Mobile applications from inception to deployment. Proficient in Android development, IOS development, HTML, CSS, JavaScript, React, Angular, MySQL, and MongoDB, he’s committed to enhancing user experiences through intuitive websites and advanced mobile applications.

Full Stack Developer Course Banner