0
0
C++programming~15 mins

Array size and bounds in C++ - Deep Dive

Choose your learning style9 modes available
Overview - Array size and bounds
What is it?
An array is a collection of elements stored in a sequence in memory. The size of an array is the number of elements it can hold. Bounds refer to the valid range of indexes you can use to access elements in the array, starting from zero up to size minus one. Accessing outside these bounds leads to undefined behavior.
Why it matters
Knowing the size and bounds of an array prevents errors like reading or writing outside the allocated memory, which can crash programs or cause security issues. Without understanding bounds, programs can behave unpredictably, making debugging very hard and risking data corruption.
Where it fits
Before learning arrays, you should understand variables and basic data types. After mastering array size and bounds, you can learn about pointers, dynamic arrays, and container classes like std::vector that handle size automatically.
Mental Model
Core Idea
An array is like a row of numbered mailboxes where each mailbox holds one item, and the size tells you how many mailboxes there are, while bounds are the valid mailbox numbers you can use.
Think of it like...
Imagine a row of mailboxes numbered from 0 to N-1. You can only put or take mail from these numbered boxes. Trying to use a mailbox number outside this range is like trying to open a door that doesn't exist.
Array of size 5:
┌───┬───┬───┬───┬───┐
│ 0 │ 1 │ 2 │ 3 │ 4 │  <-- valid indexes (bounds)
├───┼───┼───┼───┼───┤
│ A │ B │ C │ D │ E │  <-- elements
└───┴───┴───┴───┴───┘
Build-Up - 7 Steps
1
FoundationWhat is an array in C++
🤔
Concept: Introduce the basic idea of arrays as fixed-size collections of elements of the same type.
In C++, an array is declared by specifying the type and the number of elements. For example: int numbers[5]; creates an array of 5 integers. The elements are stored contiguously in memory.
Result
You have a block of memory reserved for 5 integers, accessible by indexes 0 to 4.
Understanding that arrays hold multiple values of the same type in a fixed-size block is the foundation for working with collections in C++.
2
FoundationArray indexing and zero-based counting
🤔
Concept: Explain how array elements are accessed using zero-based indexes.
Array elements are accessed using square brackets and an index starting at 0. For example, numbers[0] accesses the first element, numbers[4] the last in a size 5 array. Indexes outside 0 to 4 are invalid.
Result
You can read or write elements at valid indexes, like numbers[2] = 10; sets the third element to 10.
Knowing that array indexes start at zero is crucial to avoid off-by-one errors and accessing invalid memory.
3
IntermediateDetermining array size with sizeof operator
🤔Before reading on: do you think sizeof(array) gives the number of elements or the total bytes? Commit to your answer.
Concept: Use the sizeof operator to find the total size in bytes of the array and calculate the number of elements.
sizeof(numbers) returns the total bytes used by the array. Dividing by sizeof(numbers[0]) (size of one element) gives the number of elements. For example: int size = sizeof(numbers) / sizeof(numbers[0]);
Result
You get the correct number of elements in the array, which is 5 in this case.
Understanding how sizeof works with arrays helps you find their size at compile time, avoiding hardcoding numbers.
4
IntermediateWhat happens when accessing out-of-bounds
🤔Before reading on: do you think accessing numbers[5] causes a compile error, runtime error, or undefined behavior? Commit to your answer.
Concept: Explain that accessing indexes outside valid bounds does not cause compile errors but leads to undefined behavior at runtime.
If you try to read or write numbers[5] in a size 5 array, the program may crash, produce garbage data, or silently corrupt memory. The compiler does not check bounds for built-in arrays.
Result
Undefined behavior can cause crashes or bugs that are hard to find.
Knowing that C++ does not protect you from out-of-bounds access explains why careful bounds checking is essential.
5
IntermediateArrays decay to pointers in expressions
🤔Before reading on: do you think arrays keep their size information when passed to functions? Commit to your answer.
Concept: Explain that arrays decay to pointers when passed to functions, losing size information.
When you pass an array to a function, it becomes a pointer to the first element. The function cannot know the array size unless you pass it separately. For example: void func(int arr[]) { /* arr is a pointer */ }
Result
You must manage array size manually in functions to avoid out-of-bounds errors.
Understanding array decay clarifies why functions need size parameters and why built-in arrays are limited.
6
AdvancedUsing std::size for safer array size retrieval
🤔Before reading on: do you think std::size works with raw arrays or only containers? Commit to your answer.
Concept: Introduce std::size from header to get array size safely and clearly.
Since C++17, std::size(array) returns the number of elements in a raw array. It is safer and clearer than manual sizeof calculations. Example: #include int size = std::size(numbers);
Result
You get the array size with less chance of mistakes.
Knowing modern C++ utilities like std::size improves code clarity and safety.
7
ExpertWhy array bounds are not checked by default
🤔Before reading on: do you think C++ could have built-in bounds checking without performance cost? Commit to your answer.
Concept: Explain the design tradeoff of C++ arrays: no bounds checking for maximum speed and control.
C++ was designed for performance and low-level control. Adding automatic bounds checks would slow down every array access. Instead, C++ leaves bounds checking to the programmer or higher-level containers like std::vector that do checks optionally.
Result
You understand why raw arrays are fast but risky, and when to use safer alternatives.
Understanding this tradeoff helps you choose the right tool for your needs and avoid common bugs.
Under the Hood
Arrays are stored as contiguous blocks of memory. The array name acts as a pointer to the first element's address. Indexing calculates the element's memory address by adding the index times the element size to the base address. No metadata about size or bounds is stored with the array itself.
Why designed this way?
C++ inherited arrays from C, designed for efficiency and close hardware control. Storing size metadata would add overhead and complexity. The language trusts programmers to manage memory carefully, enabling high performance in systems programming.
Array memory layout:
Base address -> [Element 0][Element 1][Element 2]...[Element N-1]
Indexing: address = base + index * sizeof(element)
No stored size or bounds info

Function parameter decay:
Array passed -> pointer to first element
Size info lost
Myth Busters - 4 Common Misconceptions
Quick: Does sizeof(array) always give the number of elements? Commit yes or no.
Common Belief:sizeof(array) returns the number of elements in the array.
Tap to reveal reality
Reality:sizeof(array) returns the total size in bytes, not the element count. You must divide by sizeof(element) to get the number of elements.
Why it matters:Misusing sizeof leads to wrong size calculations, causing out-of-bounds access or incomplete processing.
Quick: Does accessing array[index] outside bounds cause a compile error? Commit yes or no.
Common Belief:Accessing out-of-bounds array elements causes a compile-time error.
Tap to reveal reality
Reality:C++ does not check array bounds at compile or runtime for raw arrays, so out-of-bounds access compiles but causes undefined behavior.
Why it matters:Assuming safety leads to bugs that crash or corrupt data, making programs unreliable.
Quick: When passing an array to a function, does the function know the array size automatically? Commit yes or no.
Common Belief:Functions receiving arrays know their size automatically.
Tap to reveal reality
Reality:Arrays decay to pointers when passed to functions, losing size information. You must pass size separately.
Why it matters:Not passing size causes functions to access invalid memory, leading to crashes or data corruption.
Quick: Can std::size be used with pointers? Commit yes or no.
Common Belief:std::size works with any pointer or array-like object.
Tap to reveal reality
Reality:std::size only works with raw arrays or containers that provide size info, not with pointers.
Why it matters:Using std::size on pointers causes compilation errors or wrong assumptions about size.
Expert Zone
1
Raw arrays do not store size metadata, so size must be managed separately or inferred at compile time.
2
Array decay to pointer means that sizeof inside functions behaves differently than in the caller scope.
3
Bounds checking can be added manually or by using safer containers, but this comes with a performance cost.
When NOT to use
Avoid raw arrays when you need dynamic size, automatic bounds checking, or safer memory management. Use std::vector or std::array instead for safer and more flexible code.
Production Patterns
In production, raw arrays are mostly used for fixed-size buffers or interfacing with low-level APIs. Modern C++ code prefers std::array for fixed size and std::vector for dynamic size with built-in size and bounds safety.
Connections
Pointer arithmetic
Arrays decay to pointers and indexing uses pointer arithmetic.
Understanding how arrays relate to pointers clarifies memory access and why size info is lost in function calls.
Memory safety and buffer overflows
Array bounds violations cause buffer overflows, a common security vulnerability.
Knowing array bounds helps prevent security bugs and system crashes caused by memory corruption.
Human working memory limits
Just as humans can only hold a limited number of items in working memory, arrays have fixed sizes limiting how much data they hold.
Recognizing fixed limits in arrays parallels cognitive limits, helping appreciate why dynamic structures are needed.
Common Pitfalls
#1Accessing array elements beyond the declared size.
Wrong approach:int arr[3] = {1, 2, 3}; int x = arr[3]; // Out-of-bounds access
Correct approach:int arr[3] = {1, 2, 3}; int x = arr[2]; // Access last valid element
Root cause:Misunderstanding that valid indexes go from 0 to size-1, leading to off-by-one errors.
#2Using sizeof on array parameter inside a function to get size.
Wrong approach:void func(int arr[]) { int size = sizeof(arr) / sizeof(arr[0]); // Incorrect: arr is pointer }
Correct approach:void func(int arr[], int size) { // Use passed size parameter }
Root cause:Not realizing that arrays decay to pointers in function parameters, losing size info.
#3Assuming std::size works on pointers.
Wrong approach:int* ptr = arr; int size = std::size(ptr); // Error: ptr is not an array
Correct approach:int size = std::size(arr); // Works only on raw arrays
Root cause:Confusing pointers with arrays and their different type properties.
Key Takeaways
Arrays in C++ are fixed-size sequences of elements stored contiguously in memory.
Valid array indexes start at zero and go up to size minus one; accessing outside causes undefined behavior.
The sizeof operator returns total bytes, so divide by element size to get the number of elements.
Arrays decay to pointers when passed to functions, losing size information, so size must be passed separately.
Modern C++ offers safer alternatives like std::array and std::vector that manage size and bounds more safely.