Last 30 Days
No notifications
An array is a numbered collection of values, all of the same type, stored in contiguous memory.
int marks[5] = {78, 92, 65, 88, 70};
// ↑ ↑ ↑
// type size initial valuesprintf("%d\n", marks[0]); // 78 — first element
printf("%d\n", marks[4]); // 70 — LAST element (index 4, not 5!)
marks[2] = 100; // change 3rd element> Indexes start at 0, end at size − 1.
int a[5]; // 5 ints, values are GARBAGE
int b[5] = {0}; // 5 ints, all zero
int c[5] = {1, 2, 3}; // {1, 2, 3, 0, 0} — rest zeroed
int d[] = {10, 20, 30, 40}; // size inferred → 4int n = 5;
int arr[5] = {10, 20, 30, 40, 50};for (int i = 0; i < n; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
int a[10];
int n = sizeof(a) / sizeof(a[0]); // 10> ⚠️ This trick only works on real arrays in scope, not on pointers (more on that in Pointers).
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
printf("%d\n", matrix[1][2]); // 6Arrays are the most fundamental data structure in computing. Every list, every string, every image is — at some level — an array. Understanding them in C means understanding memory itself.
When you write int arr[5];, the compiler reserves 5 × 4 = 20 bytes of contiguous memory:
Memory: [ arr[0] ][ arr[1] ][ arr[2] ][ arr[3] ][ arr[4] ]
Address: 1000 1004 1008 1012 1016Indexing arr[i] is just address-of-arr + i × sizeof(int). That's why array access is O(1) — one multiply and one add, regardless of size.
This contiguous layout also makes arrays incredibly cache-friendly: when the CPU loads one element, it usually pulls neighbours into the cache for free.
C does not check array bounds. Ever. This is fast — and dangerous.
int a[5];
a[10] = 42; // ❌ writes to memory that isn't yours
// might silently corrupt another variable
// might crash with a segfault
// might do nothing visible — until laterThis is *the* most common source of bugs and security holes in C code. You are responsible for staying inside 0 .. size-1.
> Modern compilers + sanitizers (-fsanitize=address) will catch many of these at runtime — turn them on while learning.
int a[5]; // values: GARBAGE (whatever was in RAM)
int b[5] = {0}; // values: 0 0 0 0 0
int c[5] = {1, 2}; // values: 1 2 0 0 0 (rest zeroed)
int d[5] = {[2] = 99}; // values: 0 0 99 0 0 (designated initialiser, C99+)/* For local arrays, ALWAYS initialise. Reading garbage is undefined behaviour. */
Global arrays are automatically zero-initialised — local ones are not.
int n = 10;
int arr[n]; // ✅ C99 VLA (Variable-Length Array) — supported but discouraged
int arr2[CONST]; // ✅ if CONST is a #define or constant expressionFor dynamic sizes, use malloc (covered in Pointers / Memory). VLAs are easy to overflow the stack.
int a[10];
printf("%zu\n", sizeof(a)); // 40 (10 × 4 bytes)
printf("%zu\n", sizeof(a)/sizeof(a[0])); // 10 (count of elements)This works only when a is a real array in the same scope. The moment you pass an array to a function, it decays to a pointer and sizeof returns the pointer's size (8 bytes on 64-bit), not the array's size:
void bad(int a[]) {
printf("%zu\n", sizeof(a)); // 8 — NOT what you want!
}Lesson: when passing arrays, always pass the size as a separate parameter:
void good(int a[], int n) {
for (int i = 0; i < n; i++) ...
}int grid[3][4]; // 3 rows × 4 columns
grid[1][2] = 7; // row 1, column 2Memory layout is row-major in C: row 0 is stored first, then row 1, then row 2:
[0][0] [0][1] [0][2] [0][3] [1][0] [1][1] ...Loop in this order for cache efficiency:
for (int r = 0; r < 3; r++) /* outer = row */
for (int c = 0; c < 4; c++) /* inner = column */
grid[r][c] = 0;/* Sum */
int sum = 0;
for (int i = 0; i < n; i++) sum += a[i];/* Find max */
int max = a[0];
for (int i = 1; i < n; i++)
if (a[i] > max) max = a[i];
/* Linear search */
int idx = -1;
for (int i = 0; i < n; i++)
if (a[i] == target) { idx = i; break; }
/* Reverse in place */
for (int i = 0, j = n - 1; i < j; i++, j--) {
int tmp = a[i]; a[i] = a[j]; a[j] = tmp;
}
| Bug | Symptom |
| Reading uninit local array | Random garbage values |
a[size] instead of a[size-1] for last | Out-of-bounds — undefined |
| Forgetting array size in function | sizeof returns pointer size |
Comparing arrays with == | Compares addresses, not contents |
| Returning a local array from a function | Memory invalid after return |
Master arrays and the next steps — strings, pointers, dynamic memory — fall into place.