Introduction

An array stores elements of one type in consecutive memory locations, so the address of any element can be computed from its indices with a little arithmetic. That computation gives O(1)O(1) access by index, the property that makes arrays the foundation of heaps, hash tables, matrices and dynamic programming tables.

This note centres on address calculations: given the base address, element size, index bounds and storage order, find the address of an element. A second topic is how to store special matrices (triangular, symmetric, tridiagonal, sparse) compactly and where a given element lands. This note derives the general formulas and checks each one by enumeration. How C arrays relate to pointers is in Arrays and Their Relation to Pointers.

Conventions: BB is the base address (address of the first element), ww the size of one element in bytes (addressing is by bytes unless addresses are counted in words). Index ranges are written l..ul..u, so a dimension has u−l+1u - l + 1 elements. C arrays always start at index 0; other lower bounds come from languages such as Pascal and Fortran, and from 1-based presentations.

Prerequisites

Core concepts

One-dimensional arrays

For A[l..u]:

addr(A[i])=B+(i−l) w,number of elements=u−l+1.\text{addr}(A[i]) = B + (i - l)\,w, \qquad \text{number of elements} = u - l + 1 .

In C (l=0l = 0) this is B + i * sizeof A[0], exactly what &A[i] computes.

Two-dimensional arrays: row-major and column-major

For A[l1..u1][l2..u2] with n1=u1−l1+1n_1 = u_1 - l_1 + 1 rows and n2=u2−l2+1n_2 = u_2 - l_2 + 1 columns:

  • Row-major (C, C++, Pascal): all of row l1l_1, then row l1+1l_1 + 1, and so on.
addr(A[i][j])=B+((i−l1) n2+(j−l2)) w\text{addr}(A[i][j]) = B + \big((i - l_1)\,n_2 + (j - l_2)\big)\,w
  • Column-major (Fortran, MATLAB): all of column l2l_2, then the next column, and so on.
addr(A[i][j])=B+((j−l2) n1+(i−l1)) w\text{addr}(A[i][j]) = B + \big((j - l_2)\,n_1 + (i - l_1)\big)\,w

In words: count how many complete rows (or columns) come before the element, multiply by the row (or column) length, and add the position inside the current row (or column).

Row-major versus column-major address calculation
Figure 1. A 3 by 4 array laid out in memory in row-major order (rows one after another) and in column-major order (columns one after another), with element A[1][2] (0-based) at linear offset 6 in row-major and 7 in column-major.

Three or more dimensions

For A[l1..u1][l2..u2][l3..u3] in row-major order, with nk=uk−lk+1n_k = u_k - l_k + 1:

addr(A[i][j][k])=B+(((i−l1) n2+(j−l2)) n3+(k−l3)) w.\text{addr}(A[i][j][k]) = B + \Big(\big((i - l_1)\,n_2 + (j - l_2)\big)\,n_3 + (k - l_3)\Big)\,w .

The pattern generalises: in row-major order the last index varies fastest; in column-major order the first index varies fastest. The first dimension's size n1n_1 never appears in the row-major formula, which is why a C function parameter can omit only the first dimension (int a[][4]).

How it works: special matrices

Square n×nn \times n matrices with many guaranteed zeros (or repeated entries) can be stored in a one-dimensional array holding only the significant entries. In this section indices are 1-based, 1≤i,j≤n1 \le i, j \le n, and the formulas give the 0-based offset kk into the 1D array; the address is B+k wB + k\,w.

Lower triangular matrix (A[i][j]=0A[i][j] = 0 for j>ij > i)

Number of stored entries: 1+2+⋯+n=n(n+1)/21 + 2 + \cdots + n = n(n+1)/2.

  • Row-major: rows 1..i−11..i-1 contain 1+2+⋯+(i−1)1 + 2 + \cdots + (i-1) entries, so
k=i(i−1)2+(j−1),j≤i.k = \frac{i(i-1)}{2} + (j - 1), \qquad j \le i .
  • Column-major: columns 1..j−11..j-1 contain n+(n−1)+⋯+(n−j+2)n + (n-1) + \cdots + (n - j + 2) entries, so
k=(j−1) n−(j−1)(j−2)2+(i−j),j≤i.k = (j-1)\,n - \frac{(j-1)(j-2)}{2} + (i - j), \qquad j \le i .

Upper triangular matrix (A[i][j]=0A[i][j] = 0 for i>ji > j)

  • Row-major: k=(i−1) n−(i−1)(i−2)2+(j−i)k = (i-1)\,n - \dfrac{(i-1)(i-2)}{2} + (j - i), for i≤ji \le j.
  • Column-major: k=j(j−1)2+(i−1)k = \dfrac{j(j-1)}{2} + (i - 1), for i≤ji \le j.

Note the symmetry: upper triangular column-major has the same shape of formula as lower triangular row-major with ii and jj exchanged.

Symmetric matrix (A[i][j]=A[j][i]A[i][j] = A[j][i])

Store only the lower triangle (n(n+1)/2n(n+1)/2 entries) and access A[i][j]A[i][j] with j>ij > i as A[j][i]A[j][i].

Lower-triangular and band matrix storage
Figure 2. A 4 by 4 lower triangular matrix packed row by row into a 10-element array, and a 5 by 5 tridiagonal matrix packed row by row into a 13-element array, with the offset of each stored entry written in its cell.

Tridiagonal and band matrices

A tridiagonal matrix has non-zero entries only where ∣i−j∣≤1|i - j| \le 1: the main diagonal and the diagonals directly above and below. It has 3n−23n - 2 such entries. Stored row by row (row 1 has 2 entries, rows 2 to n−1n-1 have 3, row nn has 2):

k=2i+j−3,∣i−j∣≤1.k = 2i + j - 3, \qquad |i - j| \le 1 .

A band matrix of positive order n≥1n\ge1 and integer half-width 0≤b≤n−10\le b\le n-1, with nonzeros allowed only where ∣i−j∣≤b|i-j|\le b, has n(2b+1)−b(b+1)n(2b+1)-b(b+1) allowed positions. Tridiagonal is b=1b=1 for n≥2n\ge2. If a redundant larger width is supplied, use beff=min⁡(b,n−1)b_{\mathrm{eff}}=\min(b,n-1) in the formula.

Sparse matrices

When only a few entries are non-zero but with no fixed pattern, store a list of triplets (row, column, value), usually sorted by row and then column, or use linked lists per row. For tt non-zero entries the triplet form uses 3t3t values instead of n2n^2; it saves space when 3t<n23t < n^2 (counting each stored value as one unit).

Worked examples

The C row-major offsets below are for C11 with gcc on x86-64 Linux (LP64, 4-byte int).

Example 1 (2D with non-zero lower bounds). A[-3..4][5..10] of 4-byte integers, B=1000B = 1000. Then n1=8n_1 = 8, n2=6n_2 = 6.

  • Row-major: addr(A[1][7])=1000+((1−(−3))⋅6+(7−5))⋅4=1000+26⋅4=1104\text{addr}(A[1][7]) = 1000 + ((1 - (-3)) \cdot 6 + (7 - 5)) \cdot 4 = 1000 + 26 \cdot 4 = 1104.
  • Column-major: 1000+((7−5)⋅8+(1−(−3)))⋅4=1000+20⋅4=10801000 + ((7 - 5) \cdot 8 + (1 - (-3))) \cdot 4 = 1000 + 20 \cdot 4 = 1080.

The C check models the array as int M[8][6] with A[i][j] at M[i+3][j-5]; the byte offset of M[4][2] is 104, and sizeof M is 192.

Example 2 (3D). A[1..3][1..4][1..5] of 2-byte elements, row-major, B=2000B = 2000: addr(A[2][3][4])=2000+(((2−1)⋅4+(3−1))⋅5+(4−1))⋅2=2000+33⋅2=2066\text{addr}(A[2][3][4]) = 2000 + (((2-1) \cdot 4 + (3-1)) \cdot 5 + (4-1)) \cdot 2 = 2000 + 33 \cdot 2 = 2066. In C, &T[1][2][3] in int T[3][4][5] is 33 elements from the start.

Example 3 (lower triangular, n=6n = 6). Element A[5][3]A[5][3]: row-major offset 5⋅4/2+2=125 \cdot 4/2 + 2 = 12; column-major offset 2⋅6−1+2=132 \cdot 6 - 1 + 2 = 13. The packed array has 6⋅7/2=216 \cdot 7/2 = 21 entries; a strictly lower triangular matrix (zero diagonal too) would need 1515.

Example 4 (tridiagonal). For any nn, A[1][1]A[1][1] is at offset 0, A[2][1]A[2][1] at 2⋅2+1−3=22 \cdot 2 + 1 - 3 = 2, and A[i][i]A[i][i] at 3i−33i - 3.

Important properties and observations

  • Access by index is O(1)O(1) because the address is a fixed formula of the indices.
  • Insertion or deletion in the middle of an array costs O(n)O(n) element moves; a linked list avoids the moves but loses O(1)O(1) indexing.
  • Searching an unsorted array is O(n)O(n); a sorted array supports binary search in O(lg⁡n)O(\lg n) (Searching).
  • Traversing a 2D array in its storage order (row by row for C) touches memory sequentially, which matters for caches (Arrays and Their Relation to Pointers).
  • A complete binary tree stored in an array uses index arithmetic in the same spirit: children of index ii at 2i2i and 2i+12i + 1 in a 1-based heap (Binary Heaps).
  • When a question gives addresses in words rather than bytes, take ww in words.

GATE examination perspective

  • Address of an element for given bounds, element size, base and order.
  • Reverse problems: given the addresses of two elements, find the base address or a dimension.
  • Triangular, symmetric and tridiagonal storage: number of entries, and the offset of a given element in row- or column-major packing.
  • Row-major versus column-major in a loop nest: which order of loops accesses memory sequentially.
  • Sparse matrices: triplet representation and its space.

Summary

Sources

  1. D. E. Knuth, The Art of Computer Programming, Vol. 1, 3rd ed., Addison-Wesley, 1997: Section 2.2.6 (arrays and orthogonal lists: sequential allocation of arrays, triangular matrices).
  2. ISO/IEC 9899:2011 (C11), committee draft N1570, (consulted 2026-11-09): Section 6.5.2.1p3 (multidimensional arrays are stored in row-major order).

Next steps