1

I am having some problems with double pointers as arguments in C.

From what I know so far: when I have a function that takes a pointer as an argument, say a function called functionX(int *y) then when I call functionX, let's say by: functionX(&randomvar),where randomvar is an integer containing a certain value (let's say 5), then C will create a pointer that is also called 'randomvar' that contains the address of randomvar. So the output of *randomvar will be 5.

Is my understanding correct?

If so, then when the function has double pointers : functionX(int **y) and by doing functionX(&randomvar) creates 2 pointers, one that contains the address of randomvar, another that contains the address of the first pointer.

I'm a bit stumped here I'm not sure if it's correct.

Wyck
  • 10,311
  • 6
  • 39
  • 60
Ryan Tee
  • 59
  • 5
  • 1
    A double pointer is a pointer to a pointer. That's it. They're not special. To do `functionX(&randomvar)`, `randomvar` must be a pointer. You can't do `&&randomvar` because `&` works with variables and `&randomvar` is not a variable. – user253751 Mar 11 '20 at 19:07
  • Question from earlier today provides general discussion and may help [Pointer to pointer of structs indexing out of bounds](https://stackoverflow.com/questions/60629732/pointer-to-pointer-of-structs-indexing-out-of-bounds-when-i-try-to-index-anyt) – David C. Rankin Mar 11 '20 at 19:09

6 Answers6

3

From what I know so far: when I have a function that takes a pointer as argument, says a function called functionX(int *y) then when I call functionX, let's say by: functionX(&randomvar),where randomvar is an integer containing a certain value (let's say 5), then C will create a pointer that is also called 'randomvar' that contains the addresse of randomvar. So the ouput of *randomvar will be 5.

Is my understanding correct?

No.

The situation you describe would be along these lines:

void functionX(int *y) {
    // ...
}

int main(void) {
    int randomvar = 5;
    functionX(&randomvar);
}

The expression &randomvar in the main() function of that code does evaluate to the address of that function's local variable randomvar. The expression has type int *, the same as parameter y to function functionX(), so it is well suited for use as an argument to that function. All well and good so far.

But the expression &randomvar designates only a value, not an object. There is no storage reserved for it, and therefore it does not have an address. It also has no name, and in particular, it is not named 'randomvar'. Inside function functionX(), (a copy of) that value can be accessed as y, and the expression *y will evaluate to 5. Nowhere in the code presented is *randomvar a semantically valid expression.

If so

It is not so.

, then when the function has double pointers : functionX(int **y) and by doing functionX(&randomvar) creates 2 pointers, one that contains the adresse of randomvar, another that contains the adresse of the first pointer.

Not at all. &randomvar is a single expression. Evaluating it produces one value, which, as I've already covered, has type int *. This is does not match a function parameter of type int **. An int ** is a pointer to an int *. To obtain one, you might take the address of an object (not a value) of type int *:

void functionY(int **z) {
    // ...
}

int main(void) {
    int randomvar = 5;
    int *varptr = &randomvar;
    functionY(&varptr);
}

When done that way, there are indeed two pointers, one an int * and the other an int **, but the former needs to be declared explicitly.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • Alright it's much clearer now thanks alot! By the way, in your example since functionY takes in argument of type 'int *' which only takes in a value, why is '&varptr' considered a value and not an adresse of an object of type 'int *'? – Ryan Tee Mar 11 '20 at 20:03
  • My apologies, @RyanTee, that was a typo in the declaration of `functionY()` -- a distinctly consequential one, in fact, much to my embarrassment. Fixed now. – John Bollinger Mar 11 '20 at 20:26
2

Visualize it this way:

void foo(int a);
void bar(int *b);
void baz(int **c);

// main function
{
    int x = 5;
    int *p = &x;

    foo(x);
    bar(p);
    baz(&p);
}
// **main** mem space
virtual mem address    var name (conceptual)    value
===================    =====================    =====
ABCD:4000                  x                    5
ABCD:4008                  p                    ABCD:4000
// **foo** mem space
virtual mem address    var name (conceptual)    value
===================    =====================    =====
BCDE:2000                  a                    5
// a new variable is created.
// any changes made on 'a' will not affect 'x' in 'main'
// **bar** mem space
virtual mem address    var name (conceptual)    value
===================    =====================    =====
BCDE:4000                  b                    ABCD:4000
// a new pointer is created pointing to 'x'
// 'b' points to 'x' and '*b' means 'the value stored in ABCD:4000'
// any changes made on '*b' will affect 'x' in main
// **baz** mem space
virtual mem address    var name (conceptual)    value
===================    =====================    =====
BCDE:8000                  c                    ABCD:4008
// a new pointer is created pointing to 'p'
// 'c' points to 'p' and '*c' means 'the value stored in ABCD:4008'
// any changes made on '*c' will change the value of 'p' in main
// if '**c = 7' is executed, x will be assigned '7' 
// if '*c = ABCD:8000' is executed, p will no longer point to 'x'
ssd
  • 2,340
  • 5
  • 19
  • 37
1

No, your understanding is not correct. C doesn't "create" anything in the sense you assumed -- most variables are simply labels of memory locations (not only though, they may "alias", or label, CPU registers) -- your randomvar in the scope where functionX is called is a label of a memory area allocated to store an integer and which is interpreted as integer. The value of &randomvar unary expression (operator & with a single operand randomvar) is the address of the data. That address is what is passed to functionX as y, meaning that a memory location (or CPU register) is reserved and stores the address (not the value) of randomvar.

As an example, say your program declares a variable like int randomvar; -- when executed, a part of RAM -- 4 bytes for an int typically -- is reserved to hold the variable value of randomvar. The address isn't known until the program is executing in memory, but for the sake of the example let's imagine the address 0xCAFEBABEDEADBEEF (8 bytes) is the one that points to the 4 bytes to hold the integer value. Before the variable is assigned a value, the value at the address is indeterminate -- a declaration of a variable only reserves the space to hold the value, it doesn't write anything at the address, so before you assign a value to the variable, you shouldn't even be using the value at all (and most C compilers can warn you about this).

Now, when the address is passed to functionX this means that for the function, label y is 8 bytes reserved at some memory location, to store an address of an integer variable. When called like functionX(&randomvar), y stores 0xCAFEBABEDEADBEEF. However, y also [typically] has an address -- the former value (address of randomvar) has to be stored somewhere! Unless a CPU register stores the value, in which case there is no [RAM] address, naturally.

For a pointer to a pointer like int * * y, y labels a memory location reserved to store an address to an address of an integer variable.

Armen Michaeli
  • 8,625
  • 8
  • 58
  • 95
1

hen when I call functionX, let's say by: functionX(&randomvar),where randomvar is an integer containing a certain value (let's say 5), then C will create a pointer that is also called 'randomvar' that contains the addresse of randomvar

The name of the pointer in the function functionX is y because you wrote yourself that it is the name of the function parameter functionX(int *y).

If so, then when the function has double pointers : functionX(int **y) and by doing functionX(&randomvar) creates 2 pointers, one that contains the adresse of randomvar, another that contains the adresse of the first pointer. If you have a function declared for example like

The operator & creates one pointer not two pointers.

If you have a function declared for example like

void functionX(int **y);

and a variable declared like

int randomvar = 5;

then such a call of the function like

functionX( &randomvar );

generates a compilation error because the type of the argument is int * while the type of the parameter according to the function declaration is int **.

You may not write for example like

functionX( &&randomvar );

because using the first operator & creates a temporary object of the type int *. And you may not apply the operator & to a temporary object.

To call the function you could write

int *p = &randomvar;

functionX( &p );

In this case the type of the argument will be int ** as it is required by the parameter type.

Here is a demonstrative program.

#include <stdio.h>

void f( int *p )
{
    printf( "The value of p is %p\n"
            "the pointed value is %d\n", 
            ( void * )p, *p );
}

void g( int **p )
{
    printf( "The value of p is %p\n"
            "the pointed value is also a pointer %p\n"
            "the pointed value by the dereferenced pointer is %d\n", 
            ( void * )p, ( void * )*p, **p );
}


int main(void) 
{
    int x = 5;

    f( &x );

    putchar( '\n' );

    int *p = &x;

    g( &p );

    return 0;
}

Its output might look for example like

The value of p is 0x7ffced55005c
the pointed value is 5

The value of p is 0x7ffced550060
the pointed value is also a pointer 0x7ffced55005c
the pointed value by the dereferenced pointer is 5
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

There is no pointer made of the same name as the passed variable when invoking a function with an address of a passed variable as argument. It simply passes the address of a certain variable when preceded by the & operator. Inside the called function the pointer which holds this address can have any valid identifier.


when the function has double pointers : functionX(int **y) and by doing functionX(&randomvar) creates 2 pointers, one that contains the adresse of randomvar, another that contains the adresse of the first pointer.

You cannot pass the address of an int object, which is of type int*, when the function expects an object of type int**.

Speaking for passing double pointers in general, the concept is the same as said above.

1

You can just keep chaining pointers forever if you like. Here's some code that demonstrates this:

#include<stdio.h>

void foo(int ***A, int **B, int *C) {
    printf(" %p A\n %p *A\n %p **A\n %d ***A\n", A, *A, **A, ***A);
    printf(" %p B\n %p *B\n %d **B\n", B, *B, **B);
    printf(" %p C\n %d *C\n ", C, *C);
}

int main(void) {

    int D = 8;
    int* C = &D;
    int** B = &C;
    int*** A = &B;

    foo (A,B,C);
    printf("%p &D (in main)\n", &D);
    return 0;

This will give results like this for the dereferenced pointers, each '*' comes off just as it goes on, so notice how **A = *B = C = &D

0x7ffc81b06210 A
0x7ffc81b06218 *A
0x7ffc81b06224 **A                                                                  
8 ***A                                                                              

0x7ffc81b06218 B
0x7ffc81b06224 *B
8 **B

0x7ffc81b06224 C
8 *C                                                                                

0x7ffc81b06224 &D (in main)

Note finally that the call foo (&B, &C, &D) will produce the same result.

neutrino_logic
  • 1,289
  • 1
  • 6
  • 11