DEV Community

Noah11012
Noah11012

Posted on

Double Pointer C Double Pointers in C/C++

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);
}
Enter fullscreen mode Exit fullscreen mode

Output:

~/Desktop 
 clang main.c

~/Desktop 
 ./a.out     
Value: 100
Pointer to value: 100
Doublue pointer to value: 100

~/Desktop 
 
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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!

Latest comments (14)

Collapse
 
m_aamir profile image
Aamir

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.

Collapse
 
rapidnerd profile image
George

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

Collapse
 
jamesdengel profile image
James dengel • Edited

Arrows and boxes, a good old fashioned diagram is what is called for. :)

I might take that challenge on another day.

Collapse
 
amexboy profile image
Amanu • Edited

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 ;-)

Collapse
 
dwd profile image
Dave Cridland

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 receiving struct. 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.

Collapse
 
amexboy profile image
Amanu

The cost is not intuitive, I see.

Collapse
 
noah11012 profile image
Noah11012

Maybe, but the POSIX people decided to use an output parameter.

Collapse
 
amexboy profile image
Amanu

That's not a maybe ;-)

I was asking about the design decision itself.

Thread Thread
 
noah11012 profile image
Noah11012

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.

Thread Thread
 
amexboy profile image
Amanu • Edited

A comment above made sense. It was in use doesn't justify the design.

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 receiving struct. 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.

Collapse
 
wrlee profile image
Bill Lee

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.

Collapse
 
scotthutchinson profile image
Scott Hutchinson

In C++, there is another way to code a double pointer (*&). See stackoverflow.com/a/5789842/5652483

Collapse
 
jl2210 profile image
JL2210

That's a reference to a pointer, not a double pointer.

Collapse
 
ironydelerium profile image
ironydelerium

Probably worth noting as well, one of the more common uses:

int main(int argc, char** argv)
Enter fullscreen mode Exit fullscreen mode

In this case, it's used as an array of char *, your program arguments.