2D Arrays — Declaration, Indexing, Access
A two-dimensional array organises data in a grid — rows and columns. Think of it as a spreadsheet, matrix, or table. In C, both dimensions are stated at declaration time and cannot be changed at runtime.
The first subscript is always the row. The second subscript is always the column. This order is fixed in C and must never be confused.
data_type name[rows][columns]; /* declaration */ name[row_index][col_index] = value; /* assignment */ value = name[row_index][col_index]; /* access */
int marks[3][4] — 3 rows × 4 columns = 12 elements. Access: marks[row][col]
#include <stdio.h> #define ROWS 3 #define COLS 4 int main() { int marks[ROWS][COLS]; /* declare 3×4 integer matrix */ int i, j; /* Fill using nested loops */ for (i = 0; i < ROWS; i++) for (j = 0; j < COLS; j++) marks[i][j] = (i + 1) * 10 + j; /* 10,11,12,13 / 20,21... */ /* Print in matrix format */ for (i = 0; i < ROWS; i++) { for (j = 0; j < COLS; j++) printf("%4d", marks[i][j]); printf("\n"); } /* Direct access — specific cell */ printf("marks[1][2] = %d\n", marks[1][2]); /* row 1, col 2 */ return 0; }
10 11 12 13 20 21 22 23 30 31 32 33 marks[1][2] = 22
i selects which row; the inner variable j walks across that row's columns.Memory Layout — Row-Major Order
A 2D array looks like a grid on paper, but memory is one-dimensional — a long line of bytes. C stores 2D arrays in row-major order: all elements of row 0 come first, then all of row 1, then row 2, and so on — stored as one continuous block.
This matters because accessing elements row-by-row is faster than column-by-column. Accessing in column order causes more cache misses because you jump over many memory locations with each step.
int a[3][4] in memory — row-major order — all 12 elements continuous
int a[R][C], the address of element a[i][j] = Base address + (i × C + j) × sizeof(int)Example:
a[1][2] in a[3][4] with base 1000, int=4 bytes:Address = 1000 + (1×4 + 2) × 4 = 1000 + 6×4 = 1000 + 24 = 1024
#include <stdio.h> int main() { int a[3][4]; int i, j; printf("Memory addresses of a[i][j]:\n"); printf("%-12s %-12s %-14s\n", "Element", "Address", "Offset"); printf("-------------------------------------------\n"); for (i = 0; i < 3; i++) { for (j = 0; j < 4; j++) { printf("a[%d][%d] %p +%ld bytes\n", i, j, (void*)&a[i][j], (char*)&a[i][j] - (char*)&a[0][0]); } } return 0; }
Element Address Offset ------------------------------------------- a[0][0] 0x7ffc... +0 bytes a[0][1] 0x7ffc... +4 bytes a[0][2] 0x7ffc... +8 bytes a[0][3] 0x7ffc... +12 bytes a[1][0] 0x7ffc... +16 bytes ← row 1 starts here a[1][1] 0x7ffc... +20 bytes ... a[2][3] 0x7ffc... +44 bytes ← last element
2D Array Initialisation — All Methods
/* Method 1: Row-wise with inner braces (most readable) */ int a[3][3] = { {1, 2, 3}, /* row 0 */ {4, 5, 6}, /* row 1 */ {7, 8, 9} /* row 2 */ }; /* Method 2: Flat list — filled row by row left to right */ int b[3][3] = {1, 2, 3, 4, 5, 6, 7, 8, 9}; /* Method 3: Partial — rest filled with 0 automatically */ int c[3][3] = {{1,2}, {4}}; /* c = { {1,2,0}, {4,0,0}, {0,0,0} } */ /* Method 4: All zeros — two ways */ int d[3][3] = {0}; /* all zeros */ static int e[3][3]; /* static: auto zero */ /* Method 5: Size can be omitted for rows (NOT columns) */ int f[][3] = {{1,2,3},{4,5,6}}; /* compiler counts rows = 2 */
int f[][3] is valid — compiler counts rows from the initialiser. But int f[3][] is a compile error — C must know the column size to compute offsets (address = base + i×COLS×4 + j×4).| Method | Syntax | Unspecified elements |
|---|---|---|
| Row braces | {{1,2},{3,4}} | Any missing within a row → 0 |
| Flat list | {1,2,3,4} | Remaining elements → 0 |
| Partial | {{1},{}} | Empty inner brace → all 0s for that row |
| All zero | {0} | All elements → 0 |
| Static / global | outside main() | All auto-initialised to 0 |
| Omit rows | int a[][4] | Compiler counts rows — columns must be given |
Matrix Operations — Add, Multiply, Transpose
Matrix multiplication is the most important 2D array algorithm. To multiply A[m×n] × B[n×p] = C[m×p], each element C[i][j] is the dot product of row i of A and column j of B. Requires three nested loops.
Rule: columns of A must equal rows of B. Result matrix C has rows of A and columns of B.
#include <stdio.h> #define M 2 /* rows of A */ #define N 3 /* cols of A = rows of B */ #define P 2 /* cols of B */ void printMat(int m[][P], int r, int c) { int i, j; for (i=0;i<r;i++){for(j=0;j<c;j++) printf("%5d",m[i][j]);printf("\n");} } int main() { int A[M][N] = {{1,2,3},{4,5,6}}; /* 2×3 */ int B[N][P] = {{7,8},{9,10},{11,12}}; /* 3×2 */ int C[M][P] = {0}; /* result 2×2, initialise 0 */ int i, j, k; /* C[i][j] = sum of A[i][k] * B[k][j] for all k */ for (i = 0; i < M; i++) for (j = 0; j < P; j++) for (k = 0; k < N; k++) C[i][j] += A[i][k] * B[k][j]; printf("A (2x3):\n"); printMat(A, M, N); printf("B (3x2):\n"); printMat(B, N, P); printf("C = A x B (2x2):\n"); printMat(C, M, P); return 0; }
A (2×3): B (3×2): C = A×B (2×2):
1 2 3 7 8 58 64
4 5 6 9 10 139 154
11 12
#include <stdio.h> #define N 3 void print(int m[][N], int n){ int i,j; for(i=0;i<n;i++){for(j=0;j<n;j++)printf("%4d",m[i][j]);printf("\n");} } int main() { int m[N][N] = {{1,2,3},{4,5,6},{7,8,9}}; int i, j, temp; printf("Original:\n"); print(m, N); /* Only visit upper triangle — j starts at i+1 */ /* Starting j at 0 would swap each pair TWICE → undone */ for (i = 0; i < N; i++) for (j = i + 1; j < N; j++) { temp = m[i][j]; m[i][j] = m[j][i]; m[j][i] = temp; } printf("Transposed:\n"); print(m, N); return 0; }
Original: Transposed: 1 2 3 1 4 7 4 5 6 2 5 8 7 8 9 3 6 9
Three-Dimensional Arrays
A 3D array is a collection of 2D matrices stacked as layers. Three indices: [layer][row][col]. Think of it as a book — layer = page number, row = line on page, col = character position.
- Total elements = layers × rows × cols
- Address formula: Base + (l×R×C + r×C + c) × sizeof(type)
- Real uses: RGB images (H × W × 3), video (frames × H × W), exam results (years × students × subjects)
int scores[2][3][4]; /* 2 layers × 3 rows × 4 cols = 24 ints */ scores[1][2][3] = 95; /* layer 1, row 2, col 3 */
#include <stdio.h> /* Exam scores: 2 years, 3 students, 4 subjects */ #define YEARS 2 #define STUDENTS 3 #define SUBJECTS 4 int main() { int score[YEARS][STUDENTS][SUBJECTS] = { /* Year 2023 */ {{ 80, 75, 90, 85}, { 70, 65, 80, 75}, { 95, 90, 85, 92}}, /* Year 2024 */ {{ 88, 82, 91, 86}, { 72, 78, 83, 79}, { 96, 93, 89, 97}} }; int y, s, sub, total; float avg, best = 0; int bestY = 0, bestS = 0; for (y = 0; y < YEARS; y++) { printf("=== Year %d ===\n", 2023 + y); for (s = 0; s < STUDENTS; s++) { total = 0; for (sub = 0; sub < SUBJECTS; sub++) total += score[y][s][sub]; avg = (float)total / SUBJECTS; printf(" Student %d: avg = %.1f\n", s+1, avg); if (avg > best) { best=avg; bestY=y; bestS=s; } } } printf("\nOverall best: Year %d, Student %d, avg %.1f\n", 2023+bestY, bestS+1, best); return 0; }
=== Year 2023 === Student 1: avg = 82.5 Student 2: avg = 72.5 Student 3: avg = 90.5 === Year 2024 === Student 1: avg = 86.8 Student 2: avg = 78.0 Student 3: avg = 93.8 Overall best: Year 2024, Student 3, avg 93.8
Strings — A String IS a char Array
C has no dedicated string data type. A string is simply a char array where the last element is the special null terminator character '\0' (ASCII value 0). Every C string function relies on scanning for this character to know where the string ends.
This is why strings in C are called null-terminated strings. Without the null terminator, functions like printf("%s") would read memory past the end of your string — printing garbage until it randomly finds a 0 byte.
char name[] = "Ananta" — 7 bytes: 6 characters + 1 null terminator
#include <stdio.h> #include <string.h> int main() { /* Method 1: string literal — '\0' added automatically */ char a[] = "Ananta"; /* size = 7 (auto) */ /* Method 2: character-by-character — manual '\0' */ char b[5] = {'C','o','d','e','\0'}; /* must add '\0' */ /* Method 3: buffer for input */ char c[50]; /* uninitialised — fill via scanf/fgets */ /* Proving a string is just chars */ int i; printf("Printing char by char:\n"); for (i = 0; a[i] != '\0'; i++) printf("a[%d] = '%c' ASCII = %d\n", i, a[i], a[i]); printf("\nNull terminator: a[%zu] = %d\n", strlen(a), a[strlen(a)]); printf("strlen = %zu (doesn't count '\\0')\n", strlen(a)); printf("sizeof = %zu (counts '\\0')\n", sizeof(a)); return 0; }
Printing char by char: a[0] = 'A' ASCII = 65 a[1] = 'n' ASCII = 110 a[2] = 'a' ASCII = 97 a[3] = 'n' ASCII = 110 a[4] = 't' ASCII = 116 a[5] = 'a' ASCII = 97 Null terminator: a[6] = 0 strlen = 6 (doesn't count '\0') sizeof = 7 (counts '\0')
strlen("Hello") = 5 — counts characters until '\0', does not include '\0'sizeof("Hello") = 6 — counts ALL bytes including '\0'sizeof(char name[20]) = 20 — always the declared array size, regardless of contentReading Strings — Three Methods Compared
#include <stdio.h> #include <string.h> int main() { char name[30], sentence[100]; /* ── Method 1: scanf("%s") ────────────────────────────── */ /* Reads one word only. Stops at whitespace. No overflow check. */ printf("Enter username (no spaces): "); scanf("%s", name); /* no & because array = address */ printf("Got: [%s]\n", name); /* Clear input buffer before next read */ while(getchar() != '\n'); /* ── Method 2: scanf with width limit ─────────────────── */ /* Safer: %29s reads max 29 chars (leaves room for '\0') */ printf("Enter city (max 29 chars): "); scanf("%29s", name); printf("City: [%s]\n", name); while(getchar() != '\n'); /* ── Method 3: fgets ──────────────────────────────────── */ /* BEST: reads full line with spaces, has size limit */ printf("Enter a sentence: "); fgets(sentence, sizeof(sentence), stdin); /* fgets keeps '\n' at end — remove it */ int len = strlen(sentence); if (sentence[len-1] == '\n') sentence[len-1] = '\0'; printf("Sentence: [%s]\n", sentence); printf("Length : %zu\n", strlen(sentence)); return 0; }
| Method | Reads spaces? | Overflow safe? | Keeps newline? | Verdict |
|---|---|---|---|---|
| scanf("%s") | No | No — dangerous | No | Single word only |
| scanf("%29s") | No | Yes | No | Safe single word |
| gets() | Yes | No — banned in C11 | No | Never use |
| fgets(s,n,stdin) | Yes | Yes | Yes — strip it | ✅ Recommended |
fgets() for multi-word input.string.h — Every Important Function
#include <stdio.h> #include <string.h> int main() { char s1[60] = "Hello"; char s2[] = "World"; char s3[60]; char *ptr; /* 1. strlen — count characters (not counting '\0') */ printf("strlen(\"%s\") = %zu\n", s1, strlen(s1)); /* 5 */ /* 2. strcpy — copy src into dest (dest must be big enough) */ strcpy(s3, s1); printf("strcpy: s3 = \"%s\"\n", s3); /* Hello */ /* 3. strncpy — copy at most n chars (safer than strcpy) */ char s4[10]; strncpy(s4, s2, 3); s4[3] = '\0'; /* strncpy doesn't add '\0' if n reached */ printf("strncpy 3: \"%s\"\n", s4); /* Wor */ /* 4. strcat — append s2 to end of s1 */ strcat(s1, " "); strcat(s1, s2); printf("strcat: \"%s\"\n", s1); /* Hello World */ /* 5. strcmp — compare: 0=equal, <0=s1 before s2, >0=s1 after */ printf("strcmp(\"abc\",\"abc\") = %d\n", strcmp("abc","abc")); /* 0 */ printf("strcmp(\"abc\",\"abd\") = %d\n", strcmp("abc","abd")); /* negative */ printf("strcmp(\"b\",\"a\") = %d\n", strcmp("b","a")); /* positive */ /* 6. strchr — find first occurrence of a character */ ptr = strchr(s1, 'o'); if (ptr) printf("strchr 'o': found at pos %ld\n", ptr - s1); /* 4 */ /* 7. strstr — find first occurrence of a substring */ ptr = strstr(s1, "World"); if (ptr) printf("strstr: 'World' at pos %ld\n", ptr - s1); /* 6 */ /* 8. Manual length without strlen */ int len = 0; while (s2[len] != '\0') len++; printf("Manual strlen(\"%s\") = %d\n", s2, len); /* 5 */ return 0; }
strlen("Hello") = 5
strcpy: s3 = "Hello"
strncpy 3: "Wor"
strcat: "Hello World"
strcmp("abc","abc") = 0
strcmp("abc","abd") = -1
strcmp("b","a") = 1
strchr 'o': found at pos 4
strstr: 'World' at pos 6
Manual strlen("World") = 5
| Function | Purpose | Return | Safe? |
|---|---|---|---|
| strlen(s) | Count chars before '\0' | size_t (length) | Yes |
| strcpy(d,s) | Copy s into d | pointer to d | No — use strncpy |
| strncpy(d,s,n) | Copy at most n chars | pointer to d | Yes |
| strcat(d,s) | Append s to end of d | pointer to d | No — use strncat |
| strcmp(s1,s2) | Compare lexicographically | 0 / neg / pos | Yes |
| strncmp(s1,s2,n) | Compare first n chars only | 0 / neg / pos | Yes |
| strchr(s,c) | Find first char c in s | pointer or NULL | Yes |
| strstr(s,sub) | Find substring in s | pointer or NULL | Yes |
It compares character by character using ASCII values.
"abc" vs "abd": 'c'=99, 'd'=100. Result = 99-100 = -1. This lets you sort strings alphabetically — positive means "comes after", negative means "comes before".Array of Strings — 2D char Array
To store a list of strings, use a 2D char array. First dimension = number of strings. Second dimension = maximum length of each string (including '\0'). Every row is one null-terminated string.
char names[4][10] — 4 names, max 9 chars each (+ '\0')
#include <stdio.h> #include <string.h> #define N 5 #define MAXLEN 30 int main() { char names[N][MAXLEN] = { "Vikram", "Ananta", "Sneha", "Priya", "Rahul" }; char temp[MAXLEN]; int i, j; /* Bubble sort on strings using strcmp */ for (i = 0; i < N - 1; i++) for (j = 0; j < N - i - 1; j++) if (strcmp(names[j], names[j+1]) > 0) { strcpy(temp, names[j]); strcpy(names[j], names[j+1]); strcpy(names[j+1], temp); /* swap whole strings */ } printf("Alphabetical order:\n"); for (i = 0; i < N; i++) printf("%d. %s\n", i+1, names[i]); return 0; }
Alphabetical order: 1. Ananta 2. Priya 3. Rahul 4. Sneha 5. Vikram
Word Count and Character Statistics
Count words, vowels, consonants, digits, and spaces in a sentence. Uses isalpha(), isdigit(), isspace() from ctype.h. A new word starts whenever a non-space follows a space (or is the first character).
#include <stdio.h> #include <string.h> #include <ctype.h> int isVowel(char c) { c = tolower(c); return c=='a'||c=='e'||c=='i'||c=='o'||c=='u'; } int main() { char s[200]; int words=0, vowels=0, cons=0, digits=0, spaces=0; int inWord = 0; /* flag: are we currently inside a word? */ printf("Enter sentence: "); fgets(s, sizeof(s), stdin); for (int i = 0; s[i] != '\0'; i++) { char c = s[i]; if (isspace(c)) { spaces++; inWord = 0; } else { if (!inWord) { words++; inWord = 1; } /* new word starts */ if (isdigit(c)) digits++; else if (isVowel(c)) vowels++; else if (isalpha(c)) cons++; } } printf("\n--- Statistics ---\n"); printf("Words : %d\n", words); printf("Vowels : %d\n", vowels); printf("Consonants : %d\n", cons); printf("Digits : %d\n", digits); printf("Spaces : %d\n", spaces); return 0; }
Enter sentence: Hello C 2024 is great --- Statistics --- Words : 5 Vowels : 6 Consonants : 7 Digits : 4 Spaces : 4
Caesar Cipher — Encrypt and Decrypt a String
The Caesar cipher shifts each letter by a fixed amount. Modulo 26 ensures the alphabet wraps around — 'z' shifted by 3 becomes 'c', not a character outside the alphabet. Excellent example of treating characters as numbers.
#include <stdio.h> #include <string.h> #include <ctype.h> void caesar(char *s, int shift, int encrypt) { if (!encrypt) shift = 26 - shift; /* decrypt = reverse shift */ for (int i = 0; s[i] != '\0'; i++) { if (isupper(s[i])) s[i] = (char)((toupper(s[i]) - 'A' + shift) % 26 + 'A'); else if (islower(s[i])) s[i] = (char)((tolower(s[i]) - 'a' + shift) % 26 + 'a'); /* digits and spaces unchanged */ } } int main() { char msg[100]; int key; printf("Enter message : "); fgets(msg, sizeof(msg), stdin); msg[strlen(msg)-1] = '\0'; /* remove newline */ printf("Enter shift key: "); scanf("%d", &key); printf("Original : %s\n", msg); caesar(msg, key, 1); printf("Encrypted: %s\n", msg); caesar(msg, key, 0); printf("Decrypted: %s\n", msg); return 0; }
Enter message : Hello Ananta Enter shift key: 3 Original : Hello Ananta Encrypted: Khoor Dqdqwd Decrypted: Hello Ananta
Subtract 'A' to normalise to range 0–25. Add shift. Modulo 26 wraps around. Add 'A' back. So 'Z' + shift 3 → (90-65+3)%26+65 = 28%26+65 = 2+65 = 67 = 'C' ✓
Student Report — Strings + 2D Marks Matrix
A real academic use-case: combine a 2D char array (names) with a 2D int array (marks). Computes per-student average and grade, finds class topper, and prints a formatted report card.
#include <stdio.h> #include <string.h> #define STU 5 #define SUB 4 char grade(float avg) { if (avg >= 90) return 'A'; else if (avg >= 75) return 'B'; else if (avg >= 55) return 'C'; else return 'F'; } int main() { char names[STU][20] = {"Ananta","Priya","Rahul","Vikram","Sneha"}; char subj[SUB][10] = {"Maths","Physics","C Prog","English"}; int marks[STU][SUB] = { {92,85,98,88}, {78,82,75,90}, {60,55,70,65}, {88,90,85,92}, {95,93,97,96} }; int i, j, total, topIdx = 0; float avg, topAvg = 0; printf("%-10s", "Name"); for(j=0;j<SUB;j++) printf("%-9s",subj[j]); printf("Total Avg Grade\n"); printf("----------------------------------------------------------------\n"); for (i = 0; i < STU; i++) { total = 0; for (j = 0; j < SUB; j++) total += marks[i][j]; avg = (float)total / SUB; printf("%-10s", names[i]); for (j=0;j<SUB;j++) printf("%-9d",marks[i][j]); printf("%5d %5.1f %c\n", total, avg, grade(avg)); if (avg > topAvg) { topAvg = avg; topIdx = i; } } printf("----------------------------------------------------------------\n"); printf("CLASS TOPPER: %s (%.1f average)\n", names[topIdx], topAvg); return 0; }
Name Maths Physics C Prog English Total Avg Grade ---------------------------------------------------------------- Ananta 92 85 98 88 363 90.8 A Priya 78 82 75 90 325 81.3 B Rahul 60 55 70 65 250 62.5 C Vikram 88 90 85 92 355 88.8 B Sneha 95 93 97 96 381 95.3 A ---------------------------------------------------------------- CLASS TOPPER: Sneha (95.3 average)
Quiz — IIT-Level Questions
For int a[3][4] with base address 2000, what is the address of a[2][1]? (int = 4 bytes)
What is the difference between strlen("Hello") and sizeof("Hello")?
Why is int a[][3] valid but int a[3][] is a compile error?
What does strcmp("abc", "abd") return and why?
In matrix multiplication A[m×n] × B[n×p], what is the time complexity?
What is the total memory used by char names[10][30] and what is stored in unused bytes?
Mastery Checklist
- I can declare and access a 2D array using [row][col] notation
- I can compute the memory address of a[i][j] using the row-major formula
- I know all 6 initialisation methods including omitting row count
- I know column size can never be omitted and I can explain why
- I can implement matrix addition, multiplication, and in-place transpose
- I understand why j starts at i+1 in transpose (not 0)
- I can declare, fill, and traverse a 3D array with three nested loops
- I know a string is a null-terminated char array ending in '\0' (ASCII 0)
- I know strlen does not count '\0' but sizeof does
- I always declare char array 1 byte larger than expected string length
- I use fgets for safe multi-word input and understand why gets is banned
- I can use strlen, strcpy, strncpy, strcat, strcmp, strchr, strstr
- I never use == to compare strings — always strcmp(s1,s2)==0
- I can declare char names[N][MAXLEN] and sort strings with strcmp+strcpy
- I completed all 6 quiz questions