One source of confusion among new C programmers is pointers. At first glance, there doesn't seem to be much usefulness in using them. But, it is crucial to understand pointers as it is a useful tool and any project bigger than a "Hello, World" program will have pointers. Once beginners start to grasp the concept and applicability of pointers, another wave of terror strikes deep into their hearts: double pointers.
Before moving on any further, let's recap what a pointer is.
In C and other languages like C++, a pointer is something that holds the memory address of an object. They are a numeric value and when outputted to the console they are usually presented in hexadecimal. This means, essentially, pointers are just fancy integers.
Now back to double pointers.
Upon seeing a double pointer, a beginner begins to shift uncomfortably, sweat running down their forehead all the way to their chin. Like everything else in life that is new or foreign, people feel awkward and uneasy even just slightly at the prospect of facing something they have never encountered before. But as you learn more, you start to feel confident in your new understanding of anything you desired to conquer. The same goes with double pointers.
So, what is a double pointer? Well, if a regular pointer is to refer to an object in memory, then a double pointer is a variable that points to another pointer which in turn, points to an object in memory.
Examples:
#include <stdio.h>
int main(void)
{
int value = 100;
int *value_ptr = &value;
int **value_double_ptr = &value_ptr;
printf("Value: %d\n", value);
printf("Pointer to value: %d\n", *value_ptr);
printf("Double pointer to value: %d\n", **value_double_ptr);
}
Output:
~/Desktop
➜ clang main.c
~/Desktop
➜ ./a.out
Value: 100
Pointer to value: 100
Doublue pointer to value: 100
~/Desktop
➜
When dereferencing a double pointer, we do not get the final object, but what is expected: a pointer that must be dereferenced one more time to retrieve the final value. It would be similar to the code below.
int *ptr = *value_double_ptr;
int final_value = *ptr;
Now I'm sure that most who are new to double pointers must be asking themselves the question, "Where will I ever use this?". Probably the best way to present the usefulness of double pointers is to remember one of the practical uses of regular pointers. This is to say, regular pointers can be used as an output parameter in functions. An output parameter, if you happen to not know, is a parameter that is filled with a result. Why you would use output parameters is subject to several factors and is beyond the scope of this article but I felt this is where double pointers can be utilized.
A function that we will take a look at is the POSIX getline()
function. Its purpose is to read one line from a file. When the line is read, one might suspect it to be returned, but, this is not the case as the return value is used for something different. Instead, the first argument, which takes a double pointer, is filled with the buffer that contains the line.
Why does it take a double pointer? So, that it can allocate enough memory to hold the entire line plus the null terminator character. If memory allocation and reading the one line is successful, then the pointer supplied is modified to point to the new and filled buffer. They could have only achieved this if they were using a double pointer.
When passing anything to a function, a copy is passed and not the actual object. The same goes for a pointer. If we passed a regular pointer, we could only modify the contents that the pointer points to. If we changed the pointer itself, the change would not reflect outside of the function because it is a copy.
If we want to change where the pointer points to, we have to add another indirection and take a double pointer.
An example of using getline()
might be the following.
#include <stdio.h>
int main(void)
{
FILE *file = fopen("test", "r");
char *line = 0;
int line_length = 0;
getline(&line, &line_length, file);
printf("%s\n", line);
printf("Line length: %d\n", line_length);
}
getline()
has two output parameter: one for the buffer and the other for the length of the line that was read in.
Summary
Double pointers are a challenge to many beginners immediately when first learned. As you become more comfortable with the idea of double pointers and use them when necessary in your own code, you start to think how silly that once you were afraid of double pointers.
And now I send you off with your new knowlegde!
Top comments (14)
Probably worth noting as well, one of the more common uses:
In this case, it's used as an array of
char *
, your program arguments.Couldn't
getline
have returned a struct? Why is an output parameter necessary to begin with?I think it's obvious I'm not a
C
guy, get over it ;-)A
struct
is several bytes long, and you'd need to add in the cost of getline's return value as well. These values have to be pushed onto the stack, and then pulled back off and copied again into the receivingstruct
. That costs valuable processor cycles, but also it's a hidden cost that's not intuitive from reading the code, and therefore goes against C's design.What might have been better is you have passed in a pointer to a
struct
, which would have involved fewer pushes and pops to stack - though with modern calling conventions the function arguments and return are in registers anyway, I think.The cost is not intuitive, I see.
Maybe, but the POSIX people decided to use an output parameter.
That's not a maybe ;-)
I was asking about the design decision itself.
The return value of
getline()
is the number of characters read. Because this return value is already in use, they decided an output parameter for the contents would be sufficient.A comment above made sense. It was in use doesn't justify the design.
It might be worth emphacising the value of typedefs to represent the semantics of the pointer "types". This reduces errors resulting from the confusion that your article alludes to by making double pointers look and act like single pointers. More a topic for the value of typedefs, but their usage can keep clear in one's mind, the usage and intent of the elements, pointer, double pointer, n-pointer, or no pointer.
In C++, there is another way to code a double pointer (*&). See stackoverflow.com/a/5789842/5652483
That's a reference to a pointer, not a double pointer.
I remember the first time, i saw pointers, they were mysterious and a little scary but the idea of directly dealing with the underlying memory was very captivating, so i ended up learning quite a lot about pointers.
Very well explained, remember learning them and It just blew me into oblivion. Would be interesting to see how someone could explain double pointers in the #explainlikeimfive way
Arrows and boxes, a good old fashioned diagram is what is called for. :)
I might take that challenge on another day.