📚 College Credit Guide ✓ UPI Study 🕐 8 min read

What Are Arrays Of Pointers In C?

This article explains arrays of pointers in C, how they differ from pointers to arrays, and how to declare, initialize, and use them correctly.

US
UPI Study Team Member
📅 September 12, 2026
📖 8 min read
US
About the Author
The UPI Study team works directly with students on credit transfer, degree planning, and course selection. We've helped thousands of students figure out what counts toward their degree and how to finish faster without paying more than they have to. This post is written the way we'd explain it to you directly.
🦉

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.

Programming in C
College credit · ACE & NCCRS reviewed · self-paced
View course
Close-up of colorful CSS code lines on a computer screen for web development — UPI Study

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.

ThingArray of pointersPointer to array
Exampleint *arr[5]int (*p)[5]
Meaning5 pointers1 pointer to 5 ints
Memory layout5 address slots1 contiguous block of 5 ints
Pointer matharr[i] moves by pointer size, often 8 bytes on 64-bitp+1 jumps 5 ints at once
Access pattern*arr[i] reads the target value(*p)[i] reads the i-th int
Common usestring lists, jagged datafixed 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.

Programming In C UPI Study Course

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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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.

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

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

More on Programming In C
© UPI Study. This article and its educational content are solely owned by UPI Study and licensed under CC BY-NC-ND 4.0. It is not free to reuse or modify. Any citation must credit UPI Study with a direct link to this page.