{"id":4112,"date":"2024-10-17T16:39:33","date_gmt":"2024-10-17T16:39:33","guid":{"rendered":"https:\/\/algocademy.com\/blog\/c-arrays-a-comprehensive-guide-for-beginners-and-advanced-programmers\/"},"modified":"2026-10-15T10:34:50","modified_gmt":"2026-10-15T10:34:50","slug":"c-arrays-a-comprehensive-guide-for-beginners-and-advanced-programmers","status":"publish","type":"post","link":"https:\/\/algocademy.com\/blog\/c-arrays-a-comprehensive-guide-for-beginners-and-advanced-programmers\/","title":{"rendered":"C++ Arrays: Declaration, Pointers, Vectors and Common Mistakes"},"content":{"rendered":"<p>A C++ array is a fixed-size block of same-typed elements stored next to each other in memory. You declare it as <code>int a[5];<\/code>, index it from 0, and the compiler does not check your index. For most new code, <code>std::vector<\/code> (size known at run time) or <code>std::array<\/code> (size known at compile time) is the better choice; plain C-style arrays still matter because you will read them in existing code and interviews ask about how they behave.<\/p>\n<h2>Declaring and initializing<\/h2>\n<p>The size must be a compile-time constant. An array declared inside a function and not initialized contains garbage, so reading it is a bug.<\/p>\n<pre><code class=\"language-cpp\">int a[5] = {1, 2, 3};   \/\/ 1 2 3 0 0 - missing elements become 0\nint b[] = {5, 2, 8, 1, 9};   \/\/ size 5 is deduced\nint z[4] = {};          \/\/ all zeros\nint local[3];           \/\/ uninitialized: indeterminate values\n<\/code><\/pre>\n<p>If the braces hold fewer values than the size, the rest are zero. If they hold more, the program does not compile. Brace initialization without <code>=<\/code> (<code>int n[5]{1, 2, 3, 4, 5};<\/code>) is equivalent. Variable-length arrays (<code>int a[n];<\/code> with a runtime <code>n<\/code>) are a C feature and a common compiler extension, but they are not standard C++; use <code>std::vector<\/code>.<\/p>\n<h2>Accessing elements and looping<\/h2>\n<pre><code class=\"language-cpp\">int numbers[5] = {1, 2, 3, 4, 5};\ncout &lt;&lt; numbers[0];   \/\/ 1\nnumbers[2] = 10;\nfor (int x : numbers) cout &lt;&lt; x &lt;&lt; ' ';   \/\/ range-for works on a real array\n<\/code><\/pre>\n<p>Valid indexes are 0 to size &#8211; 1. <code>numbers[5]<\/code> or <code>numbers[-1]<\/code> compiles and has undefined behavior: it may print garbage, crash, or appear to work and corrupt other data. Nothing in the language warns you. <code>std::vector::at(i)<\/code> is the checked alternative, and it throws <code>std::out_of_range<\/code>.<\/p>\n<p>To find the element count of a real array in the same scope, use <code>sizeof(b) \/ sizeof(b[0])<\/code> (20 \/ 4 = 5 for the array above) or, in C++17, <code>std::size(b)<\/code>. This brings us to the biggest trap.<\/p>\n<h2>Array decay: why sizeof fails inside a function<\/h2>\n<p>When you pass an array to a function, it &#8220;decays&#8221; into a pointer to its first element. The parameter <code>int arr[]<\/code> is really <code>int* arr<\/code>, so the function has no idea how long the array is.<\/p>\n<pre><code class=\"language-cpp\">int sumArr(const int arr[], size_t n) {\n    cout &lt;&lt; sizeof(arr);          \/\/ size of a pointer, not of the array\n    int s = 0;\n    for (size_t i = 0; i &lt; n; i++) s += arr[i];\n    return s;\n}\n\nint b[] = {5, 2, 8, 1, 9};\ncout &lt;&lt; sumArr(b, 5);             \/\/ 25; sizeof(arr) printed 8 on a 64-bit system\n<\/code><\/pre>\n<p>Inside <code>sumArr<\/code>, <code>sizeof(arr)<\/code> printed 8 (the size of a pointer on a 64-bit system), while <code>sizeof(b)<\/code> in the caller is 20. The compiler even warns about it. So always pass the length with the pointer, or pass a reference to a sized array (<code>const int (&amp;arr)[5]<\/code>), or use <code>std::vector<\/code>, <code>std::array<\/code> or (C++20) <code>std::span<\/code>.<\/p>\n<p>Two more consequences of decay and pointers: arrays do not copy on assignment (<code>int* q = p;<\/code> makes q point at the same data, so <code>q[0] = 99<\/code> changes <code>p[0]<\/code>), and <code>==<\/code> on two arrays compares their addresses, not their contents. Two arrays with the same values compared with <code>==<\/code> gave 0; <code>std::equal(c, c + 3, e)<\/code> gave 1. A <code>vector<\/code> copies on assignment and compares by contents.<\/p>\n<h2>Two-dimensional arrays<\/h2>\n<p>A 2D array is an array of arrays, stored row by row in one contiguous block:<\/p>\n<pre><code class=\"language-cpp\">int m[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};\ncout &lt;&lt; m[1][2];                 \/\/ 7: row 1, column 2\nint* flat = &amp;m[0][0];\ncout &lt;&lt; flat[1 * 4 + 2];          \/\/ 7 again: element (row, col) sits at row * cols + col\n<\/code><\/pre>\n<p>When you pass one to a function, every dimension except the first must be part of the parameter type (<code>int m[][4]<\/code>). That is rigid, so interview code usually uses <code>vector&lt;vector&lt;int&gt;&gt;<\/code>:<\/p>\n<pre><code class=\"language-cpp\">vector&lt;vector&lt;int&gt;&gt; grid(3, vector&lt;int&gt;(4, 0));   \/\/ 3 rows, 4 columns, all 0\ngrid[1][2] = 7;\n<\/code><\/pre>\n<p>Iterate rows in the outer loop and columns in the inner loop; that matches the memory layout of the C-style array and is faster for large grids.<\/p>\n<h2>Choosing: C array, new[], vector or std::array<\/h2>\n<table>\n<thead>\n<tr>\n<th>Type<\/th>\n<th>Size known<\/th>\n<th>Knows its size<\/th>\n<th>Copies on assignment<\/th>\n<th>Frees itself<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>int a[5]<\/code><\/td>\n<td>compile time<\/td>\n<td>no (decays)<\/td>\n<td>no<\/td>\n<td>yes (automatic storage)<\/td>\n<\/tr>\n<tr>\n<td><code>new int[n]<\/code><\/td>\n<td>run time<\/td>\n<td>no<\/td>\n<td>no (pointer copy)<\/td>\n<td>no, needs <code>delete[]<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>std::array&lt;int, 5&gt;<\/code><\/td>\n<td>compile time<\/td>\n<td>yes<\/td>\n<td>yes<\/td>\n<td>yes<\/td>\n<\/tr>\n<tr>\n<td><code>std::vector&lt;int&gt;<\/code><\/td>\n<td>run time, can grow<\/td>\n<td>yes<\/td>\n<td>yes<\/td>\n<td>yes<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>A manual dynamic array looks like this, and every <code>new[]<\/code> must be matched by exactly one <code>delete[]<\/code> (not plain <code>delete<\/code>):<\/p>\n<pre><code class=\"language-cpp\">int n = 5;\nint* d = new int[n]();   \/\/ the () zero-initializes\nd[2] = 4;\ndelete[] d;\n<\/code><\/pre>\n<p>Forgetting <code>delete[]<\/code> leaks memory, and using the pointer afterwards is undefined behavior. A <code>vector<\/code> does this for you, which is why the usual advice is to use it unless you have a specific reason not to. You still pay for one thing: <code>push_back<\/code> occasionally reallocates, which invalidates pointers and iterators into the vector.<\/p>\n<pre><code class=\"language-cpp\">vector&lt;int&gt; v = {1, 2, 3, 4, 5};\nv.push_back(6);          \/\/ size() is now 6\nv.at(10);                \/\/ throws std::out_of_range\narray&lt;int, 5&gt; st = {1, 2, 3, 4, 5};   \/\/ size() == 5, copies like a value\n<\/code><\/pre>\n<p>One <code>size()<\/code> trap applies to both: it returns an unsigned type, so on an empty vector <code>v.size() - 1<\/code> wraps around to 18446744073709551615 on a 64-bit system. Write <code>i + 1 &lt; v.size()<\/code> instead of <code>i &lt; v.size() - 1<\/code>, or cast to <code>int<\/code>.<\/p>\n<p>To practice these basics in small steps, AlgoCademy&#8217;s C++ course has lessons on <a href=\"https:\/\/algocademy.com\/link\/?lang=cpp&amp;problem=static-arrays&amp;solution=1\">static arrays<\/a>, <a href=\"https:\/\/algocademy.com\/link\/?lang=cpp&amp;problem=arrays&amp;solution=1\">vectors<\/a> and <a href=\"https:\/\/algocademy.com\/link\/?lang=cpp&amp;problem=exceeding-static-array-bounds&amp;solution=1\">exceeding array bounds<\/a>.<\/p>\n<h2>Standard algorithms that work on arrays<\/h2>\n<p>Pointers into an array are valid iterators, so the standard algorithms work directly on C arrays. Include <code>&lt;algorithm&gt;<\/code>:<\/p>\n<pre><code class=\"language-cpp\">int b[] = {5, 2, 8, 1, 9};\nsort(b, b + 5);                         \/\/ 1 2 5 8 9\nbool has8 = binary_search(b, b + 5, 8); \/\/ true; needs sorted input\nauto it = find(b, b + 5, 8);            \/\/ it - b == 3\nauto lb = lower_bound(b, b + 5, 5);     \/\/ first element &gt;= 5; lb - b == 2\nreverse(b, b + 5);                      \/\/ 9 8 5 2 1\n<\/code><\/pre>\n<p>With a vector, use <code>v.begin()<\/code> and <code>v.end()<\/code> in place of <code>b<\/code> and <code>b + 5<\/code>. Prefer these over hand-written bubble sort or linear search outside of learning exercises: <code>sort<\/code> runs in O(n log n), bubble sort in O(n^2). Writing binary search yourself is still a common interview task. Compute the midpoint as <code>left + (right - left) \/ 2<\/code> to avoid overflow, and note that it requires sorted input (there is a <a href=\"https:\/\/algocademy.com\/link\/?lang=cpp&amp;problem=binary-search&amp;solution=1\">Binary Search lesson<\/a> for it).<\/p>\n<h2>Array problems that come up in interviews<\/h2>\n<p>These three cover most beginner questions. All were compiled and run on the examples shown.<\/p>\n<h3>Two Sum<\/h3>\n<p>Find two indexes whose values add to a target. A hash map from value to index gives O(n) time:<\/p>\n<pre><code class=\"language-cpp\">vector&lt;int&gt; twoSum(const vector&lt;int&gt;&amp; nums, int target) {\n    unordered_map&lt;int, int&gt; seen;\n    for (int i = 0; i &lt; (int)nums.size(); i++) {\n        auto it = seen.find(target - nums[i]);\n        if (it != seen.end()) return {it-&gt;second, i};\n        seen[nums[i]] = i;\n    }\n    return {};\n}\n\/\/ twoSum({2, 7, 11, 15}, 9) returns {0, 1}\n<\/code><\/pre>\n<h3>Rotate an array right by k<\/h3>\n<p>Three reversals do it in place in O(n) time and O(1) extra space. Reduce <code>k<\/code> first, and note that an empty array would divide by zero in <code>k %= size<\/code>.<\/p>\n<pre><code class=\"language-cpp\">void rotateRight(vector&lt;int&gt;&amp; nums, int k) {\n    k %= (int)nums.size();\n    reverse(nums.begin(), nums.end());\n    reverse(nums.begin(), nums.begin() + k);\n    reverse(nums.begin() + k, nums.end());\n}\n\/\/ {1,2,3,4,5,6,7} with k = 3 becomes {5,6,7,1,2,3,4}\n<\/code><\/pre>\n<h3>Maximum subarray (Kadane&#8217;s algorithm)<\/h3>\n<p>At each element, either extend the current run or start a new one:<\/p>\n<pre><code class=\"language-cpp\">int maxSubArray(const vector&lt;int&gt;&amp; nums) {\n    int best = nums[0], cur = nums[0];\n    for (size_t i = 1; i &lt; nums.size(); i++) {\n        cur = max(nums[i], cur + nums[i]);\n        best = max(best, cur);\n    }\n    return best;\n}\n\/\/ {-2,1,-3,4,-1,2,1,-5,4} returns 6 (the subarray 4, -1, 2, 1)\n<\/code><\/pre>\n<p>There is a longer walkthrough in <a href=\"https:\/\/algocademy.com\/blog\/kadanes-algorithm-mastering-the-maximum-subarray-problem\/\">Kadane&#8217;s algorithm<\/a>, and <a href=\"https:\/\/algocademy.com\/blog\/strategies-for-solving-array-manipulation-problems\/\">strategies for array manipulation problems<\/a> covers the wider set of patterns.<\/p>\n<h2>Checklist of common mistakes<\/h2>\n<ul>\n<li>Reading or writing index <code>size<\/code> (off by one). Loop with <code>i &lt; size<\/code>, not <code>&lt;=<\/code>.<\/li>\n<li>Using an uninitialized local array.<\/li>\n<li>Taking <code>sizeof<\/code> of a function parameter and expecting the element count.<\/li>\n<li>Comparing arrays with <code>==<\/code>, or expecting assignment to copy them.<\/li>\n<li>Mixing <code>new[]<\/code> with <code>delete<\/code>, or forgetting to free.<\/li>\n<li>Holding a pointer or reference into a vector across a <code>push_back<\/code>.<\/li>\n<li>Subtracting from <code>size()<\/code> on a possibly empty container.<\/li>\n<li>Allocating a very large array as a local variable: the stack is small (often a few megabytes), so put big arrays in a <code>vector<\/code>, or make them <code>static<\/code> or global.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>How C++ arrays work: declaring and initializing, 2D arrays, array decay, new[] vs vector vs std::array, and the interview patterns to know, with tested code.<\/p>\n","protected":false},"author":1,"featured_media":4111,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[23],"tags":[],"class_list":["post-4112","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-problem-solving"],"_links":{"self":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/4112"}],"collection":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/comments?post=4112"}],"version-history":[{"count":1,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/4112\/revisions"}],"predecessor-version":[{"id":8740,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/posts\/4112\/revisions\/8740"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/media\/4111"}],"wp:attachment":[{"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/media?parent=4112"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/categories?post=4112"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/algocademy.com\/blog\/wp-json\/wp\/v2\/tags?post=4112"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}