Nearby lessons

95 of 124

C - Array of Pointers

An array of pointers holds addresses instead of values. Learn the int *a[5] declaration, why it is the natural way to store a list of strings, and how it differs from a pointer to an array.

Declaring an Array of Pointers

Read it from the name outwards: a is an array of 5, of pointers to int:

Example01
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int x = 10, y = 20, z = 30;
6 
7 int *a[3]; /* three pointers, currently uninitialised */
8 
9 a[0] = &x;
10 a[1] = &y;
11 a[2] = &z;
12 
13 for (int i = 0; i < 3; i++)
14 printf("a[%d] points to %d\n", i, *a[i]);
15 
16 printf("Array size: %zu bytes (3 pointers)\n", sizeof(a));
17 return 0;
18}
Output
a[0] points to 10
a[1] points to 20
a[2] points to 30
Array size: 24 bytes (3 pointers)

Initialising at Declaration

Supply the addresses in a brace list, exactly like any other array:

Example02
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int a = 1, b = 2, c = 3;
6 int *ptrs[3] = {&a, &b, &c};
7 int i;
8 
9 /* Read through the pointers */
10 for (i = 0; i < 3; i++) printf("%d ", *ptrs[i]);
11 printf("\n");
12 
13 /* Write through them */
14 for (i = 0; i < 3; i++) *ptrs[i] *= 10;
15 
16 printf("a=%d b=%d c=%d\n", a, b, c);
17 return 0;
18}
Output
1 2 3
a=10 b=20 c=30

The Main Use — A List of Strings

This is where an array of pointers earns its keep. Each string is exactly as long as it needs to be:

Example03
CCode Cell
1#include <stdio.h>
2#include <string.h>
3 
4int main()
5{
6 const char *fruits[5] = {"Apple", "Banana", "Cherry",
7 "Watermelon", "Fig"};
8 int i;
9 
10 for (i = 0; i < 5; i++)
11 printf("%-12s (%zu chars)\n", fruits[i], strlen(fruits[i]));
12 
13 printf("\nArray itself: %zu bytes (5 pointers)\n", sizeof(fruits));
14 return 0;
15}
Output
Apple        (5 chars)
Banana       (6 chars)
Cherry       (6 chars)
Watermelon   (10 chars)
Fig          (3 chars)

Array itself: 40 bytes (5 pointers)

Compared with a 2D char Array

A 2D array gives every string the same width, so short strings waste space. An array of pointers does not — but the strings are read-only:

char names[5][20]char *names[5]
Memory used100 bytes, fixed40 bytes + the literals
Wasted spaceYes, for short stringsNone
Modifiable textYesNo, if pointing at literals
Reorder the listMust copy stringsJust swap pointers
ContiguousYesNo
Example04
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 char fixed[3][20] = {"Al", "Bernadette", "Chen"};
6 const char *flexible[3] = {"Al", "Bernadette", "Chen"};
7 
8 printf("2D array : %zu bytes\n", sizeof(fixed));
9 printf("Array of pointers: %zu bytes (plus literals)\n",
10 sizeof(flexible));
11 
12 fixed[0][0] = 'X'; /* modifiable */
13 printf("Modified 2D: %s\n", fixed[0]);
14 /* flexible[0][0] = 'X'; would crash - read-only literal */
15 return 0;
16}
Output
2D array         : 60 bytes
Array of pointers: 24 bytes (plus literals)
Modified 2D: Xl

Sorting Without Moving Text

In simple words: to reorder strings held by pointers you swap 8-byte addresses instead of copying dozens of characters. The text never moves — only the list of where to find it changes.
Example05
CCode Cell
1#include <stdio.h>
2#include <string.h>
3 
4int main()
5{
6 const char *names[5] = {"Rahul", "Amit", "Priya", "Zara", "Karan"};
7 const char *temp;
8 int i, j;
9 
10 for (i = 0; i < 4; i++)
11 for (j = 0; j < 4 - i; j++)
12 if (strcmp(names[j], names[j + 1]) > 0)
13 {
14 temp = names[j]; /* swap POINTERS */
15 names[j] = names[j + 1];
16 names[j + 1] = temp;
17 }
18 
19 printf("Sorted: ");
20 for (i = 0; i < 5; i++) printf("%s ", names[i]);
21 printf("\n");
22 return 0;
23}
Output
Sorted: Amit Karan Priya Rahul Zara 

A Menu or Lookup Table

A common practical use: a fixed table of labels indexed by a value:

Example06
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 const char *days[7] = {"Sunday", "Monday", "Tuesday", "Wednesday",
6 "Thursday", "Friday", "Saturday"};
7 const char *menu[4] = {"Add record", "View records",
8 "Delete record", "Exit"};
9 int i;
10 
11 printf("Day 3 is %s\n\n", days[3]);
12 
13 printf("MENU\n");
14 for (i = 0; i < 4; i++)
15 printf(" %d. %s\n", i + 1, menu[i]);
16 return 0;
17}
Output
Day 3 is Wednesday

MENU
  1. Add record
  2. View records
  3. Delete record
  4. Exit

Passing an Array of Pointers to a Function

The parameter becomes a char ** — the same type as main's argv:

Example07
CCode Cell
1#include <stdio.h>
2#include <string.h>
3 
4void printAll(const char *list[], int n)
5{
6 int i;
7 for (i = 0; i < n; i++) printf("%d. %s\n", i + 1, list[i]);
8}
9 
10const char *longest(const char *list[], int n)
11{
12 int i;
13 const char *best = list[0];
14 
15 for (i = 1; i < n; i++)
16 if (strlen(list[i]) > strlen(best)) best = list[i];
17 return best;
18}
19 
20int main()
21{
22 const char *cities[4] = {"Delhi", "Mumbai", "Bengaluru", "Pune"};
23 
24 printAll(cities, 4);
25 printf("Longest: %s\n", longest(cities, 4));
26 return 0;
27}
Output
1. Delhi
2. Mumbai
3. Bengaluru
4. Pune
Longest: Bengaluru

Array of Pointers vs Pointer to Array

One asterisk, one pair of parentheses, two entirely different types. The parentheses change everything:

DeclarationMeanssizeof
int *a[5]An array of 5 pointers40 bytes
int (*a)[5]One pointer to an array of 5 ints8 bytes
Example08
CCode Cell
1#include <stdio.h>
2 
3int main()
4{
5 int data[5] = {1, 2, 3, 4, 5};
6 
7 int *arrayOfPointers[5]; /* 5 pointers */
8 int (*pointerToArray)[5]; /* 1 pointer */
9 
10 for (int i = 0; i < 5; i++) arrayOfPointers[i] = &data[i];
11 pointerToArray = &data;
12 
13 printf("Array of pointers : %zu bytes, a[2] -> %d\n",
14 sizeof(arrayOfPointers), *arrayOfPointers[2]);
15 printf("Pointer to array : %zu bytes, (*p)[2] = %d\n",
16 sizeof(pointerToArray), (*pointerToArray)[2]);
17 return 0;
18}
Output
Array of pointers : 40 bytes, a[2] -> 3
Pointer to array  : 8 bytes, (*p)[2] = 3

An Array of Allocated Blocks

Each pointer can own a separately allocated block — a jagged array where every row has its own length:

Example09
CCode Cell
1#include <stdio.h>
2#include <stdlib.h>
3 
4int main()
5{
6 int *rows[3];
7 int lengths[3] = {2, 4, 3}; /* different sizes! */
8 int i, j;
9 
10 for (i = 0; i < 3; i++)
11 {
12 rows[i] = malloc(lengths[i] * sizeof(int));
13 if (rows[i] == NULL) return 1;
14 for (j = 0; j < lengths[i]; j++) rows[i][j] = (i + 1) * (j + 1);
15 }
16 
17 for (i = 0; i < 3; i++)
18 {
19 printf("Row %d (%d items): ", i, lengths[i]);
20 for (j = 0; j < lengths[i]; j++) printf("%d ", rows[i][j]);
21 printf("\n");
22 }
23 
24 for (i = 0; i < 3; i++) free(rows[i]);
25 return 0;
26}
Output
Row 0 (2 items): 1 2
Row 1 (4 items): 2 4 6 8
Row 2 (3 items): 3 6 9 

Common Mistakes

  • Forgetting to initialise the pointersint *a[5]; holds five garbage addresses.
  • Modifying a string literalnames[0][0] = 'X' crashes when names[0] points at a literal.
  • Confusing int *a[5] with int (*a)[5] — check for the parentheses.
  • Using sizeof to count strings — it counts pointers, not characters.
  • Freeing the array instead of the blocks — each allocated block needs its own free.
  • strcpy into a literal pointer — there is no writable space there.
An array of pointers to literals is read-only. char *names[] = {"Rahul"}; then strcpy(names[0], "Priya"); compiles cleanly and then segfaults — you are writing into the program's constant data. Declare the array as const char * so the compiler stops you, or use a 2D array when the text must change.
📝 Key Takeaways
  • int *a[5] is an array of 5 pointers to int.
  • char *names[] is the standard way to hold a list of strings.
  • Each string can be a different length — no wasted space.
  • Sorting strings means swapping pointers, not copying text.
  • int *a[5] and int (*a)[5] are completely different types.

🧠 Test Your Knowledge

4 Questions
Progress: 0 / 4