Arrays of pointers in C store addresses, not the actual values. That means each slot in the array points to something else, like an int, a char, a struct, or a string. This is different from a normal array of values, where the data sits right inside the array memory. The idea sounds small, but the syntax trips up a lot of students in programming in C. One missing pair of parentheses can change int *arr[5] into int (*p)[5], and those two declarations do very different jobs. In one case, you have 5 pointers. In the other, you have 1 pointer to a block of 5 ints. That difference matters because C does not hold your hand. You need to know whether you are moving across 5 addresses or across 5 stored values. If you mix those up, pointer arithmetic goes off the rails fast, and the bug can look random for 20 minutes before you spot the real issue. Arrays of pointers show up all the time in string lists, lookup tables, and jagged 2D data. They also show up in many programming in c course examples because they teach memory layout in a very clean way. Once you can read the declaration, you can read the code. That is the whole trick.
What Are Arrays Of Pointers In C?
An array of pointers in C is a 1D array whose 5 or 50 elements each hold an address instead of a value. So if you write int *arr[4], each slot can point to a separate int, char, struct, or string, and the array itself only stores 4 addresses.
That memory idea matters. A normal array like int nums[4] keeps 4 ints right next to each other, but an array of pointers keeps 4 pointer values that may point anywhere in memory. One pointer might point to a stack variable, one to heap memory from malloc, one to a string literal, and one to a struct in a different place. That scattered layout gives you flexibility, but it also makes mistakes easier.
The catch: the array does not own the data it points to, so the data can outlive the array, or vanish before it does. That is why dangling pointers show up so often in C labs and code reviews.
For strings, the pattern looks especially useful. A list like char *names[3] can hold "Ava", "Mina", and "Ravi" without copying 3 separate character blocks into one big buffer. You get a clean list of 3 addresses, and each address leads to a string that can have a different length, like 2 characters or 20.
This style also fits a table of function names or error messages. In programming in C, people use it because it keeps each element independent, which helps when one item needs 8 bytes and another needs 80. The downside? You must track each target carefully, because the array gives you addresses, not safety. That is the tradeoff, and C never hides it.
How Do Arrays Of Pointers Differ From Pointer To Array?
The difference comes down to what the name points to: separate pointer slots or one whole block. That one shift changes declaration syntax, pointer math, and how 5 elements sit in memory, so this is not a tiny grammar issue. It changes what your code can legally do.
| Thing | Array of pointers | Pointer to array |
|---|---|---|
| Example | int *arr[5] | int (*p)[5] |
| Meaning | 5 pointers | 1 pointer to 5 ints |
| Memory layout | 5 address slots | 1 contiguous block of 5 ints |
| Pointer math | arr[i] moves by pointer size, often 8 bytes on 64-bit | p+1 jumps 5 ints at once |
| Access pattern | *arr[i] reads the target value | (*p)[i] reads the i-th int |
| Common use | string lists, jagged data | fixed rows, 2D blocks |
Reality check: students miss the parentheses because C makes both versions look almost the same at first glance. That is a bad design choice, honestly, but it is the language you get.
If you see int *arr[5], read it as "array of 5 pointers." If you see int (*p)[5], read it as "pointer to an array of 5 ints." That habit saves time in a programming in c course and cuts down on ugly pointer bugs.
Learn Programming In C Online for College Credit
This is one topic inside the full Programming In C course on UPI Study — a self-paced, online class that earns real college credit. Credits are ACE and NCCRS evaluated and transfer to partner colleges across the US and Canada. Courses start at $250 with no deadlines and lifetime access.
See Programming In C Course →How Do You Declare Arrays Of Pointers In C?
The syntax works in three steps: pick the base type, add * to make each element a pointer, then wrap the whole thing in [size]. Once you learn that pattern, declarations like int *x[10] and char *words[12] stop looking strange after 2 or 3 tries.
- Start with the data type you want each pointer to reference. If you want ints, write int; if you want text, write char; if you want anything, write void.
- Add one * for each element, not for the whole array. int *arr[5] means 5 int pointers, while int (*arr)[5] means something else entirely.
- Pick the size in brackets after the pointer part. A 5-element array of pointers gives you 5 separate address slots, and each slot can point somewhere different.
- Use char *names[3] for strings when you want 3 text values like "Sam", "Leah", and "Noah". That pattern shows up in 80% of beginner string-list examples because it is easy to read.
- Use void *items[4] only when you need mixed target types and you can handle casts carefully. That choice saves space in code, but it adds type checks you must manage yourself.
- Watch the parentheses. int (*p)[5] gives you 1 pointer to 5 ints, and many students only catch the difference after a 45-minute debugging session.
How Do You Initialize And Access Arrays Of Pointers?
Initialization can happen at declaration, by later assignment, or inside a loop, and each style fits a different 5-minute job. With strings, you often point each slot at a literal or at a separate char buffer; with numeric data, you usually point at existing ints or heap blocks. The big rule is simple: arr[i] gives you an address, while *arr[i] gives you the value at that address. That split matters even more when some slots hold NULL, because a NULL entry marks an empty spot without fake data.
- char *words[3] = {"cat", "dog", "fox"}; stores 3 addresses to 3 string literals.
- int a = 10, b = 20; int *nums[2] = {&a, &b}; points to 2 existing ints.
- Use *nums[0] to read 10, and nums[0] itself to get the address of a.
- NULL entries help when only 7 of 10 slots are ready, especially in staged loading.
- In loops, arr[i] = &values[i]; gives you 1 address per step, fast and clear.
What this means: you can build a list of pointers without copying the real data at all, which keeps memory use smaller when strings run 40 or 200 bytes each. That is one reason programmers like this pattern for menus, names, and small lookup tables.
The downside shows up fast if you point at dead stack data or free a heap block too early. Then arr[2] still holds an address, but that address no longer leads to valid data. C lets you do that, which is exactly why pointer bugs feel so rude.
Why Use Arrays Of Pointers In Real C Programs?
Arrays of pointers help when each item has a different size, and that happens all the time in real C code. A list of 6 command names, 12 city names, or 4 error messages fits well because each string can stay separate instead of living in one fixed-size 2D block.
They also work well for jagged data. Suppose one row has 3 grades and another has 8; an array of pointers lets each row point to a different-length block, which beats wasting space on a flat 10-by-10 grid. That style shows up in lookup tables, parsers, and dispatch code where a name maps to a function pointer. Clean idea. Sharp edges.
Worth knowing: arrays of pointers are not the same as double pointers, even if both use ** in some code. A double pointer often points to one pointer variable, while an array of pointers stores 5 or 20 pointer elements right inside the array. That mix-up causes real bugs, especially when malloc and free enter the picture.
Common mistakes hit hard: dangling pointers after a local array dies, mismatched allocation when you free the wrong block, and confusing an array of 8 pointers with 1 pointer to 8 items. I see that error pattern more than I should, and it usually starts with a declaration read too fast. A 2-second pause before you type the brackets saves a 2-hour chase later.
Frequently Asked Questions about Arrays Of Pointers
An array of pointers in C is an array whose elements are pointer variables. Each element stores an address, not a direct value. For example, int *ptrs[3]; creates three int pointers. This structure is useful when you want one list to reference multiple separate objects, such as strings, dynamically allocated blocks, or rows in a 2D-like setup.
Use the type, then an asterisk with the element type, then the array name and size. For example, char *names[5]; declares an array of five pointers to char. int *p[10]; declares an array of ten pointers to int. The asterisk belongs to the pointer type, not to the array itself.
An array of pointers, such as int *a[4];, is an array containing four int pointers. A pointer to an array, such as int (*p)[4];, is one pointer that points to an entire array of four ints. Parentheses matter. These are different types, layouts, and access patterns in programming in C.
int *p[10] means p is an array of 10 pointers to int. Each p[i] can point to an int variable, an element inside a larger array, or dynamically allocated memory. It does not mean a single pointer to 10 ints. That distinction is important in a programming in C course.
You can initialize it with addresses or string literals. For example, int a=1,b=2; int *p[2]={&a,&b};. For strings, char *words[] = {"red", "green", "blue"};. Each element stores an address. If the pointers are uninitialized, they contain garbage values and must not be dereferenced.
Use the array index first, then dereference if needed. For example, p[0] gives the first pointer, and *p[0] gives the int it points to. With strings, words[1] is a char pointer to the second string, and words[1][0] is the first character of that string. This is common in arrays of in C pointer managing.
Arrays of pointers are a common way to store multiple strings because each string can have a different length. char *names[] = {"Ana", "Ben", "Chris"}; lets each pointer refer to a separate null-terminated string. This is simpler and more flexible than using a fixed-size 2D character array when lengths vary.
The array itself is one contiguous block holding pointer values. Each pointer may refer to a different location elsewhere in memory. For example, in char *words[] = {"one", "two"}; the array stores two addresses, while the actual strings may be in read-only memory or another allocated region. The data is not necessarily contiguous.
Yes. You can store addresses returned by malloc in an array of pointers. For example, int *p[3]; p[0]=malloc(sizeof(int));. This is useful when each element needs separate storage. You must free each allocated block later. This pattern is common in programming in C and memory management exercises.
Common mistakes include confusing int *p[5] with int (*p)[5], dereferencing uninitialized pointers, and assuming the pointed-to objects are contiguous. Another mistake is modifying string literals through char * pointers, which is undefined behavior in modern C. Always verify each pointer is valid before use and manage allocation carefully.
An array of pointers can simulate a 2D structure, but it is not the same as a true 2D array. A true 2D array like int a[3][4] is one contiguous block. An array of pointers like int *rows[3] can point to separate rows of different lengths. This makes it more flexible but less uniform.
Understanding arrays of pointers helps students read and write real C code involving strings, tables, and dynamic memory. It is a core topic in programming in C course material and appears often in exams and labs. Strong grasp of declaration syntax, access rules, and memory layout supports success in online course, college credit, and transferable credit settings.
Final Thoughts on Arrays Of Pointers
Arrays of pointers in C look confusing until you separate the two ideas: an array stores many pointer values, while a pointer to an array stores one address for a whole block. Once that clicks, declarations like int *arr[5] and int (*p)[5] stop feeling like trick questions. The real payoff comes from use. String lists, uneven row sizes, lookup tables, and function dispatch code all get easier when each item can point somewhere different. That flexibility beats a fixed 2D block whenever your data sizes do not match neatly, like 3 names in one list and 14 in another. The trap sits in the details. arr[i] gives you an address, *arr[i] gives you the value, and NULL tells you a slot has no target yet. Miss that once, and you get bugs that look fine on the surface but fail the moment memory shifts. Keep the syntax slow and exact. Read the stars. Read the brackets. Then write the declaration only after you can say what every part means out loud.
How UPI Study credits actually work
Ready to Earn College Credit?
ACE & NCCRS approved · Self-paced · Transfer to colleges · $250/course or $99/month