Arrays and pointers are the bread and butter of programming in C that give developers optimized memory access and flexibility in code. While arrays let you organize data, pointers provide direct access to memory addresses that store this data.
Combined together into an array of pointers, they pave the way for building efficient data structures and modular programs in C.
In this comprehensive guide, you‘ll learn:
- Fundamentals of arrays and pointers in C
- Syntax and techniques to create arrays of pointers
- Populating array of pointers with addresses of data
- Accessing data values through the array of pointers
- Passing arrays of pointers to functions
- Common use cases and applications
I have also provided step-by-step C code examples, visual diagrams, and analysis of the pros and cons of using array of pointers in your projects to boost learning.
So let‘s get started!
A Quick Refresher: Arrays in C
Before going into using an array of pointers, it‘s helpful to recap how arrays work in C programming.
An array represents a contiguously allocated set of elements of the same data type. The elements are accessed by their index, with indexes starting from 0.
Some key properties of arrays in C:
- Arrays have a fixed size that you need to define on initialization
- They allow random access of elements using index
- Accessing elements is fast due to contiguous memory allocation
To declare an array in C, you use the following syntax:
dataType arrayName[arraySize];
For instance, to make an integer array called data
that can store 40 elements:
int data[40];
We can also initialize arrays with values:
int values[3] = {10, 68, 99}; // Initialized 3 values
And access elements using indexes:
int first = values[0]; // Access first item
values[1] = 20; // Modify 2nd element
Visualizing array contents stacked sequentially in memory
Knowing arrays is key before learning the array of pointers concept.
Now let‘s breakdown how pointers work in C.
Understanding Pointers in C
A pointer in C programming is a variable that stores the address of another variable or data storage value. Think of it as directing to the location of some data value rather than containing the actual value itself.
Primarily, pointers serve two important roles:
- They allow passing arguments by reference into functions instead of just by value
- Help access memory addresses directly for optimized performance
Let‘s see how to declare and use pointers step-by-step:
Declaring Pointers
To define a pointer variable, the *
symbol is appended before the variable name to mark it as a pointer:
int* ptr; // ptr is declared as pointer to an integer
This allocates the memory and associates ptr
as an integer pointer variable that can now store memory addresses rather than just values.
Getting Memory Address
We can get the memory address of a variable using the address-of (&) operator like:
int num = 55;
ptr = # // Assign address of num to pointer ptr
Dereferencing Pointers
To retrieve the actual value stored at the address contained by a pointer, we dereference it using the *
operator:
printf("Value of num: %d", *ptr); // Print dereferenced value
When ptr
contains the address of num
, then *ptr
represents the actual int
value stored there i.e. 55.
Fig 2: Visualizing how a pointer obtains address and points to value
These three steps demonstrate the core pointer mechanism that sets the foundation for more complex pointer techniques.
Having covered arrays and pointers independently, now let‘s look at why pointers can be gamechanging when combined into pointer arrays.
Why Use Pointer Arrays in C?
The array of pointers technique combines the key strengths of arrays and pointers to unlock more efficient and modular C programs:
✔️ Access sequence of memory addresses: Storing addresses itself instead of just data values provides direct access to memory for high performance. This is faster than looping arrays and doing index access.
✔️ Pass addresses directly to functions: We can pass just the pointer array to provide the callee access to necessary data without copying entire objects.
✔️ Dynamic memory allocation: We can dynamically allocate memory at given addresses referenced by array elements. This facilitates expandable data rather than fixed arrays.
✔️ Implement custom data structures: Array of pointers helps build linked lists, graph data structures, trees, etc. leading to more optimized code with faster access.
✔️ Better memory utilization: Lesser memory needed with just addresses instead of all redundant data. Results in lighter and faster applications.
These advantages enable developers to tap into arrays and pointers to engineer specialized data handling logic as per program requirements.
Now let‘s get hands-on into building array of pointers from scratch.
Creating Array of Pointers in C
The process mirrors creating normal arrays, except now the data type is specified as a pointer using the *
symbol:
dataType* arrayName[arraySize];
Here:
datatype*
marks the array elements as pointersarrayName
gives a label to access the array[arraySize]
allocates sequential memory for pointers
For instance, to declare an array called ptrArr
to hold 5 pointers to int
:
int* ptrArr[5];
Allocates space for 5 integer pointers.
We can initialize it along with declaration as well:
double* ptrArr[3] = {NULL, NULL, NULL}; // Initializes 3 double pointers to NULL
Sets each element to null pointer state.
And that‘s it! You have declared an array of pointer variables.
Now let‘s see how to populate it to actually reference memory addresses.
Populating Array of Pointers
The next step is to populate each pointer element with a memory address it should get mapped to.
This is done by:
- Declaring primitive data variables
- Obtaining address of variables using
&var
- Saving these addresses into the array of pointers
Consider this example code:
#include <stdio.h>
int main() {
int num1 = 10, num2 = 20, num3 = 30; // Variables declared
int* ptrArr[3]; // Integer pointer array
ptrArr[0] = &num1;
ptrArr[1] = &num2;
ptrArr[2] = &num3;
return 0;
}
Here, ptrArr
array elements will now contain addresses of each num
variable respectively.
We have essentially mapped memory spaces to be referred indirectly by the pointer array.
Accessing Array of Pointer Values
The final part is utilizing the array of pointers to retrieve data values dynamically from addresses.
We simply have to:
- Index the pointer array
- Deference using
*
to get actual integer value
Considering the previous example, we can print values:
#include <stdio.h>
int main() {
int num1 = 10, num2 = 20, num3 = 30;
int* ptrArr[3];
// Array population
for(int i=0; i<3; i++) {
printf("Value: %d\n", *ptrArr[i] ); //Dereference
}
return 0;
}
Prints:
Value: 10
Value: 20
Value: 30
We are able to fetch values stored inside addresses populated within the pointer array.
Passing Pointer Array to Functions
A common application of using the array of pointers approach is passing data addresses directly to functions:
void printElements(int* ptrArr[], int arrSize) {
// Access ptrArr elements
for(int i=0; i<arrSize; i++) {
printf("Value: %d\n", *ptrArr[i] ); //Dereference
}
}
Now we can simply pass the populated pointer array as an argument:
int main() {
int* ptrArr[3];
populateArray(ptrArr); // Populates addresses
printElements(ptrArr, 3);
return 0;
}
This provides functions direct access to necessary data without having to copy large arrays or variables.
In high-performance applications, this is extremely useful.
Heap vs Stack Allocation
Now that you have a firm grasp on utilizing array of pointers, let‘s compare the differences between declaring pointer arrays in stack vs heap memory.
Stack Allocated Pointer Arrays
We have used the simpler method of direct declaration so far which allocates memory for pointer array from program stack:
int* ptrArr[5]; // Stack-allocated
Pros:
- Faster performance
- Easy to implement
Cons:
- Limited size, risk of overflows
- Deallocates after function returns
This works in most cases. But for dynamic scenarios, we need to leverage heap allocation.
Heap Allocated Pointer Arrays
For a flexible pointer array, we can dynamically allocate it from heap using malloc()
:
int n = 5; // Size needed
int** ptrArr = malloc(n * sizeof(int*)); // Allocates memory
Pros:
- Flexible size as needed
- Persists outside functions
Cons:
- Slower than stack
- Risk of memory leaks
- Manual de-allocation
Heap gives you more control on sizing pointer arrays as needed by your program flow and data.
Building Linked Lists with Pointer Arrays
One common use case for pointer arrays is building data structures like linked lists. Here is a simple implementation:
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
}
// Creates linked list from array of pointers
struct Node** createLinkedList(int arr[], int n){
struct Node** headArr = malloc(sizeof(struct Node*) * n);
struct Node* current, *next;
// Populate nodes
for(int i=0; i<n; i++){
current = malloc(sizeof(struct Node));
current->data = arr[i];
if(i < n-1){
current->next = malloc(sizeof(struct Node));
next = current->next;
}
else
current->next = NULL;
headArr[i] = current;
}
return headArr;
}
This creates a linked list by allocating each node dynamically and connecting pointers. The headArr
here serves as array of pointers to nodes.
We can pass this lightweight linked list structure easily to other functions rather than entire arrays.
This brings out key areas where pointer arrays can be extremely useful.
Conclusion & Key Takeaways
The array of pointers approach fuses the core concepts of arrays and pointers to enable building specialized data structures optimized for performance and modular code.
Wrapping up the key learnings:
✔️ Arrays allow sequential access while pointers provide direct memory access
✔️ Pointer arrays combine these to store addresses optimally
✔️ Helps pass data indirectly without copying entire chunks
✔️ Enables building efficient linked data structures like graphs and trees
✔️ Choosing between stack vs heap allocation offers flexibility
With this deep dive into pointer arrays, now you can unleash the speed and modularity they offer compared to regular arrays in your C programs.
I hope this guide gives you a firm grasp on wielding this powerful technique to take your C coding skills to build performant systems to the next level.
Let me know if you have any other questions!