C++ Notes
0.int scores[10]; declares an array of size 10, so valid indices run from 0 to 9.=: scores[0] = 33;, scores[1] = 45;| Form | Result |
|---|---|
int arr[5]{10, 20, 30, 40, 50}; | Fully initialized at declaration using braces. |
int arr[5]{1, 2}; | Partial brace initialization. arr[0]=1, arr[1]=2, every remaining element becomes 0. |
int arr[5]{}; | All elements zero-initialized. No garbage values, predictable and safe. This is the preferred practice. |
int arr[]{1, 2, 3}; | Size is deduced by the compiler from the initializer list. Valid, but an explicit size is usually clearer to read. |
const is not enough on its own. Only literals or constexpr values work.| Invalid | Valid |
|---|---|
| int x = 5; int arr[x]; // runtime variable size_t n = 5; int arr[n]; // runtime variable const int x = 5; int arr[x]; // const != compile-time |
constexpr int x = 5; int arr[x]; // compile-time constant int arr[5]; // literal size int arr[]{1, 2, 3}; // deduced size |
Some compilers like g++ may allow runtime-sized arrays as an extension, but this is not standard C++. Relying on it causes "works on my machine" problems the moment the code gets compiled somewhere else, like on a different compiler or in a CI pipeline that follows the standard strictly.
#include <iostream> int main() { // 1. Declaration (garbage values at each index) int scores[10]; // 2. Assignment after declaration (only = allowed) scores[0] = 33; scores[1] = 45; // 3. Declaration + assignment (brace initialization) int arr1[5]{10, 20, 30, 40, 50}; // 4. Partial brace initialization (remaining elements become 0) int arr2[5]{1, 2}; // 5. Zero-initialized array (recommended, no garbage values) int arr3[5]{}; // 6. Size deduction by compiler (valid but less readable) int arr4[]{1, 2, 3}; // 7. Accessing elements by index std::cout << "arr1[0]: " << arr1[0] << "\n"; // prints: 10 std::cout << "arr1[4]: " << arr1[4] << "\n\n"; // prints: 50 // 8. Enhanced for loop (read-only iteration) std::cout << "arr1 elements: "; for (int value : arr1) { std::cout << value << " "; // prints: 10 20 30 40 50 } std::cout << "\n"; // 9. Calculation using enhanced for loop int sum{0}; for (int value : arr1) { sum += value; } std::cout << "Sum of arr1: " << sum << "\n"; // prints: 150 return 0; }
std::size(arr) requires #include <iterator> and C++17 or later. Returns the element count directly.sizeof(arr) / sizeof(arr[0]) works in older standards. It divides total bytes by bytes per element to get the count.For int arr[]{1, 2, 3}; (int is 4 bytes) | Returns |
|---|---|
std::size(arr) | 3 |
sizeof(arr) | 12 (total bytes) |
sizeof(arr) / sizeof(arr[0]) | 3 |
Once you have the element count, use it in loops, pass it to functions, or use it as a boundary check.
Prefer std::size() in modern C++ (C++17 and later). It's cleaner, less error-prone, and clearly expresses intent. The sizeof division trick is a pre-C++17 workaround, kept alive mostly by codebases that predate the standard.
#include <iostream> #include <iterator> // required for std::size() in C++17 int main() { int arr[]{1, 2, 3}; std::cout << "std::size(arr): " << std::size(arr) << '\n'; // prints: 3 std::cout << "sizeof(arr): " << sizeof(arr) << '\n'; // prints: 12 (3 ints x 4 bytes each) std::cout << "sizeof(arr)/sizeof(arr[0]): " << (sizeof(arr) / sizeof(arr[0])) << '\n'; // prints: 3 return 0; }
'\0'. This tells C++ where the string ends.'\0', printing the array causes the program to keep reading memory past the array until it randomly finds a zero byte somewhere, printing garbage characters along the way.Unsafe examples: char arr1[]{'h','a','s','s','a','n'}; has no null terminator, unsafe to print. char arr2[6]{'h','a','s','s','a','n'}; has a size of exactly 6 with no room for '\0', also unsafe to print.
| Method | How |
|---|---|
| Explicit null terminator | Add '\0' manually as the last element: char arr[7]{'h','a','s','s','a','n','\0'};, safe to print. |
| Oversize by 1 and use brace init | Leave the last element unspecified. Brace initialization zeroes it automatically: char arr[7]{'h','a','s','s','a','n'};, the last element becomes '\0' from brace init, safe to print. |
| String literal initialization | The compiler appends '\0' automatically and deduces the size too: char arr[]{"Hello!"};, safe to print. |
String literal initialization is the cleanest of the three. The compiler handles both size and null termination for you, so there's nothing left to miscount.
std::cin and std::cin.getline() automatically append '\0'.std::cin.getline(variable, size). This is different from std::getline(std::cin, str), which is for std::string, not a char array.'\0'.In real code, prefer std::string over char arrays. It manages memory and null termination automatically and is far safer to work with, char arrays are worth understanding for exactly the reasons above, but they're rarely the right default choice once std::string is available.
| Expression | What It Returns |
|---|---|
std::size(char_array) | Number of elements in the array, including the null terminator slot. |
sizeof(char_array) | Memory used by the array in bytes, the same as the element count for char since each char is 1 byte. |
str.size() | Length of a std::string in characters, not counting the null terminator. |
sizeof(std::string) | Size of the string object itself in bytes, typically 32 bytes on 64-bit systems, not the length of the text it holds. |
#include <iostream> int main() { // not C-strings: no null terminator, unsafe to print char arr1[]{'h','a','s','s','a','n'}; // no '\0' char arr2[6]{'h','a','s','s','a','n'}; // size 6, no room for '\0' // std::cout << arr1; // unsafe, prints garbage // std::cout << arr2; // unsafe, prints garbage // oversize by 1: brace init zeroes the last element to '\0' char arr3[7]{'h','a','s','s','a','n'}; std::cout << arr3 << '\n'; // prints: hassan // string literal: compiler appends '\0' automatically char arr4[]{"Hello!"}; char arr5[]{"Hello, World!"}; std::cout << arr4 << '\n'; // prints: Hello! std::cout << arr5 << '\n'; // prints: Hello, World! // user input: getline for char arrays (different from std::getline for std::string) char name[20]; // large enough to hold input + '\0' std::cout << "Enter your name: "; std::cin.getline(name, 20); // reads up to 19 chars, appends '\0' automatically std::cout << "You entered: " << name << '\n'; // prints whatever was entered return 0; }
Arrays are simple until they meet raw memory: fixed size, compile-time only on the stack, and a char array is only safely printable if it's actually null terminated. Next up, pointers, where all of this stops being an implementation detail and becomes the main subject.