Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
Array of Pointers in C
Understand the array of pointers in C: how to declare it, fill it, dereference it and pass it to functions. A hands-on lesson in our free C tutorial on embedded C programming, with string tables and sensor probe lists.
In This Lecture
What You Will Learn
After pointers, arrays and multi-dimensional arrays, it is time to combine two ideas. An array of pointers in C is simply an array whose elements are pointers. It sounds like a small step, yet it is behind menu tables, command dispatchers, peripheral register lists and every table of strings in your firmware. This lecture of our free embedded C course shows you how to declare one, read and write through it, and hand it to a function safely.
- The declaration syntax and a simple rule for reading it correctly.
- How each element holds the address of some other variable, and how to reach the value through it.
- How to build a table of strings, the most common array of pointers in embedded programs.
- How to pass the table to a helper function and update the original variables.
- Which memory the pointers live in, and what to watch out for.
Prerequisites
You should know how to declare a pointer, take an address with &, and dereference with *. You also need the array fundamentals from earlier chapters and the lecture on passing arrays to functions.
What Is an Array of Pointers in C?
An ordinary array holds values, such as integers. An array of pointers holds addresses. Each element is a pointer variable and can store the address of any variable of the matching type. The declaration looks like an array declaration with one extra asterisk placed before the array name:
datatype *array_name[size];
For example, int *probe[3]; declares probe as an array of three pointers to int. A reliable way to read this: start at the name and go right first, then left. probe is an array of 3 (the brackets bind first) of pointers (the asterisk) to int. This reading order will matter a lot in the next lecture, where we compare it with a very similar-looking declaration.
An Array of Three Pointers
Notice how different this is from an array of integers. The three integers can sit anywhere in memory, in any order, even in different functions’ stack frames. The pointer array is just an index card file that records where each of them lives.
Build and Run: Sensor Probe Table
This original program models a tiny telemetry unit with three readings kept in separate variables. A pointer array lets us treat them as a list. Save it as ep_ptr_array.c.
#include <stdio.h>
#include <stddef.h>
static void trim_all(int *const list[], size_t n, int amount)
{
for (size_t i = 0; i < n; i++) {
*list[i] -= amount;
}
}
int main(void)
{
int temp_c = 36;
int vbat_mv = 3300;
int rpm = 1500;
int *probe[3] = { &temp_c, &vbat_mv, &rpm };
printf("sizeof(probe) = %zu bytes (3 pointers)\n", sizeof(probe));
printf("sizeof(probe[0]) = %zu bytes (one pointer)\n", sizeof(probe[0]));
for (int i = 0; i < 3; i++) {
printf("*probe[%d] = %d\n", i, *probe[i]);
}
*probe[1] -= 100;
printf("after *probe[1] -= 100 -> vbat_mv = %d\n", vbat_mv);
trim_all(probe, 3, 10);
printf("after trim_all(10) -> %d, %d, %d\n", temp_c, vbat_mv, rpm);
printf("probe[2] == &rpm ? %s\n", probe[2] == &rpm ? "yes" : "no");
const char *state_name[] = { "IDLE", "RUN", "FAULT" };
size_t count = sizeof(state_name) / sizeof(state_name[0]);
for (size_t s = 0; s < count; s++) {
printf("state %zu = %s\n", s, state_name[s]);
}
return 0;
}
gcc -std=c11 -Wall -Wextra -o ep_ptr_array ep_ptr_array.c
./ep_ptr_array
Expected output on a 64-bit Linux machine:
sizeof(probe) = 24 bytes (3 pointers)
sizeof(probe[0]) = 8 bytes (one pointer)
*probe[0] = 36
*probe[1] = 3300
*probe[2] = 1500
after *probe[1] -= 100 -> vbat_mv = 3200
after trim_all(10) -> 26, 3190, 1490
probe[2] == &rpm ? yes
state 0 = IDLE
state 1 = RUN
state 2 = FAULT
Reading Through an Array of Pointers
The expression probe[i] is the i-th pointer, and *probe[i] is the integer it points to. The subscript binds tighter than the star, so no parentheses are needed. Writing through it works the same way: *probe[1] -= 100 changes vbat_mv itself, as the output line shows. The array never copies the values; it only remembers where they are.
What the sizes tell you
Three pointers take 24 bytes on a 64-bit system, so sizeof(probe) is 24 and each element is 8. On a 32-bit microcontroller the figures would be 12 and 4. The size of the array depends only on the number of pointers and the pointer width, never on what they point to.
Arrays of Pointers to Strings
The second half of the program shows the most common use of an array of pointers in firmware: a table of strings. The declaration const char *state_name[] creates an array of pointers, each pointing at the first character of a string literal. The element count comes from the usual division of total size by element size, which is valid here because state_name is a real array in its own scope.
This is far better than a rectangular array of characters when the strings have different lengths. A rectangular layout must reserve space for the longest string in every row; a pointer table stores only one address per string, and the strings themselves occupy just the bytes they need.
| Feature | Array of pointers to strings | 2-D char array |
|---|---|---|
| Declaration | const char *names[] | char names[3][6] |
| Memory use | One pointer per string plus the text | Fixed width for every row |
| Mixed lengths | Efficient | Wastes space |
| Modifying a string | Not allowed for literals | Allowed (it is your own buffer) |
Where the Table Lives on a Microcontroller
For firmware, write the table as static const char *const names[] = { ... };. The second const makes the pointers themselves read-only, so the whole table can be placed in flash instead of RAM. That saves precious RAM on small devices and prevents accidental edits at run time. In our demo the table is a local variable, which is fine for learning on a PC.
Passing an Array of Pointers to a Function
The helper trim_all() receives int *const list[]. As with any array parameter, the brackets turn into a pointer, so the real type is int *const *list: a pointer to a constant pointer to int. The const says that the function will not re-point any list entry, but it may still change the integers they refer to. The function receives the array of pointers by base address, plus the count, and loops through using *list[i].
Compare that with the earlier lectures. The array being passed now has pointer elements, so the parameter has one more level of indirection, but the story is identical: only the base address of the table travels to the callee.
Two Levels of Indirection in trim_all()
Real-World Use Cases in Embedded C Programming
- Menu systems: a table of label strings for an LCD, indexed by menu position.
- Error and state names: convert numeric codes to readable log text with one array lookup.
- Peripheral lists: an array of pointers to hardware register blocks so one loop can configure several identical UARTs or timers.
- Command parsers: a table of command names, kept next to a matching table of handlers.
- Command-line arguments:
argvin a Linux program is exactly an array of pointers to strings.
Common Mistakes and Troubleshooting
| Mistake | Result | Fix |
|---|---|---|
| Dereferencing an uninitialised element | Crash or wild write | Initialise every entry, or set unused ones to NULL and test before use |
| Storing the address of a local variable that goes out of scope | Dangling pointer, garbage values later | Point only at variables that outlive the table |
| Writing through a string literal pointer | Fault, as literals sit in read-only memory | Use a char array if you need to edit text |
Writing probe[i] when you meant *probe[i] | You get an address, not the value | Dereference to reach the target |
Printing pointers with %d | Wrong output on 64-bit | Use %p with a (void *) cast |
Best Practices, Performance and Security
- Mark tables of strings
static const char *constso they stay in flash and cannot be modified. - Terminate a pointer list with
NULLwhen its length is not passed separately, the wayargvworks. - Check an index against the element count before using it, especially when it comes from a received command.
- Prefer a lookup table to a long
if–elseorswitchchain when you map numbers to text; it is smaller and faster to read. - Performance: each access costs one extra memory read, to fetch the pointer before following it, which is negligible on modern cores.
Key Takeaways
int *probe[3]is an array of three pointers toint.*probe[i]reaches the variable that element i points to.- Tables of strings are the classic array of pointers in firmware.
- The array holds addresses only; keep the targets alive as long as the table is in use.
Conclusion
An array of pointers in C is a compact way to organise things that live in scattered places: individual variables, strings of different lengths, register blocks, handlers. Declare it with the asterisk before the name, dereference through the subscripted element, and pass it to functions as a pointer to its first element together with a count. In the final lecture of this block we place it next to its look-alike, the pointer to an array, and finish with a quiz and interview questions.
Suggested Images for This Lecture
- Diagram of three pointer cells pointing at separate variables. Alt text: “array of pointers in C with each element pointing to a different variable”.
- Terminal output screenshot of the probe table demo. Alt text: “free embedded C course array of pointers in C program output”.
- Illustration of a string table in flash. Alt text: “embedded C programming array of pointers to strings stored in flash memory”.
Frequently Asked Questions
What is an array of pointers in C?
It is an array whose elements are pointer variables, each able to hold the address of a variable of the declared type, for example int *probe[3].
How do I read the value that an element points to?
Use *probe[i]. The subscript selects the pointer, and the star follows it to the target.
Why is an array of pointers good for strings?
Each element stores one address, so strings of different lengths do not waste space the way a fixed-width character matrix would.
Can the pointers in the array change at run time?
Yes, unless you declare them const. You can re-point any element to another variable, which is useful for runtime-configurable tables.
What is the parameter type when I pass such an array to a function?
Pointer to pointer. For example int *list[] becomes int **list, and int *const list[] becomes int *const *list.
Is argv an array of pointers?
Yes. In a hosted C program, argv is an array of pointers to the command-line argument strings.
Finish the Pointer Block in Our Free C Course
The next lecture contrasts the array of pointers with the pointer to an array and ends with a quiz and interview questions.
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments