Passing pointer to pointer to a function (value/reference)

May 31, 2016 at 2:47pm
Hello.

Consider this example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void byValue(int** PtoP)
{
}

void byReference(int**& PtoP)
{
}

void byAddress(int*** PtoP)
{
}

int main()
{	
	int my[] = { 2, 4, 7 };
	int my2[] = { 9, 5, 3 };

        int** RANDOM;

	int* myArray[] = { *RANDOM,  my, my2 };

	
}


I want to pass both myArray and RANDOM (which are pointer-to-pointer) both by value, reference and address.

With myArray
1
2
3
4
5
6
7
8
// OK!
byValue(myArray);

// error: cannot convert argument 1 from int *[3] to int**&	
byReference(myArray);

// error: cannot convert argument 1 from int*(*)[3] to int***
byAddress(&myArray);


Now let's try with RANDOM
1
2
3
4
5
6
// OK!
byValue(RANDOM);
// OK!
byReference(RANDOM);
// OK!
byAddress(&RANDOM);


... well ...

Will you explain me the reason of those errors?

Thanks!
Last edited on May 31, 2016 at 2:49pm
May 31, 2016 at 2:57pm
error: cannot convert argument 1 from int *[3] to int**&
You can't pass an array of T to a function that expects a reference to T *. byReference() might do this:
1
2
3
4
void byReference(int**& PtoP)
{
    PtoP = new int *;
}
But doing this would be illegal:
 
myArray = new int *;

Likewise, byAddress() might do this:
1
2
3
4
void byAddress(int*** PtoP)
{
    *PtoP = new int *;
}
May 31, 2016 at 2:57pm
When the compiler builds a pointer out of an array, that pointer is an rvalue: a non-const lvalue reference cannot be bound to it.

It's the same as with any other implicit conversion:

1
2
3
4
5
6
7
8
9
void byValue(int n) {}
void byReference(int& n) {}

int main()
{
    double d = 2.0;
    byValue(d);  // OK
    byReference(d); // error: cannot bind a reference to the rvalue int
}


You can capture that value by using a const lvalue reference
1
2
3
void byReference(int** const & PtoP)
{
}


or an rvalue reference:
1
2
3
void byReference(int**&& PtoP)
{
}



For the third ("byAddress") case, you just have two totally different pointer types: pointer to array of three pointers to int vs pointer to pointer to pointer to int.
Last edited on May 31, 2016 at 2:58pm
May 31, 2016 at 3:24pm
For the third ("byAddress") case, you just have two totally different pointer types:

pointer to an array of three pointers to int vs pointer to pointer to pointer to int.


Yeah but I'm putting the & behind myArray so I expect that int*** takes the address of myArray

Following what you said, neither the example below should have sense:

1
2
3
4
5
6
7
8
9
void func(int* b)
{}

int main()

{
     int var = 5;
     func(&var );
}


var and b are totally different: one is a variable, the other is a pointer.

But since I put the & begind var, I'm extracting the address of that variable (to make a pointer)

I followed the same logic with pointers-to-pointers.

You can't pass an array of T to a function that expects a reference to T *


This because pointers made out of an array can't be edited, right?

Last edited on May 31, 2016 at 3:25pm
May 31, 2016 at 3:43pm
I'm putting the & behind myArray so I expect that int*** takes the address of myArray

&myArray is a pointer to an array of three pointers to int. That is not int***.

In your counter-example, &var is a pointer to int, and b is also a pointer to int. What you did with myArray was more like

1
2
3
4
5
6
void func(int* b) {}

int main() {
     double var = 5.0;
     func(&var); // address of a double given, pointer to int expected
}
Last edited on May 31, 2016 at 3:43pm
May 31, 2016 at 3:57pm
&myArray is a pointer to an array of three pointers to int. That is not int***.


Ok, but!

my is an array of ints (1 level of indirection *)
my2 in an array of ints (1 level of indirection *)

RANDOM is a pointer to pointer (2 levels of indirection **)

myArray is an array of pointers
-> can be translated to ->
myArray is a pointer to the first of the 3 pointers (in short, it's a pointer to RANDOM, which in turn is a pointer) -----> (2 levels of indirection **)

We know that adding & before a variable, we can get the pointer to that variable, making ANOTHER level of indirection

1
2
3
4
5
// pointer originally has 1 level of indirection...
int* pointer;

// but, by obtaining its address, we reach another level of indirection (which is the pointerToPointer)
int** pointerToPointer = &pointer;


Isn't it the same thing with the myArray?

Originally myArray is a pointer-to-pointer.

Adding another level of indirection to it (by adding &), we should get a pointer-to-pointer-to-pointer! (3 levels of indirection!!!)
Last edited on May 31, 2016 at 4:00pm
May 31, 2016 at 4:12pm
Originally myArray is a pointer-to-pointer.

no, it's an array, it is not any kind of pointer.
May 31, 2016 at 4:15pm
no, it's an array


And an array is a pointer to its first element
May 31, 2016 at 4:24pm
gedamial wrote:
And an array is a pointer to its first element

Utter nonsense. It was one of the first things C did differently from B.
Last edited on May 31, 2016 at 4:27pm
May 31, 2016 at 4:28pm
Utter nonsense


"an array name isn't a pointer" sounds like "5 isn't an integer number" to me.

1
2
3
4
5
6
7
8
9
10
void f(int* arr)
{
}

int main()
{
    int arr[5];

    f(arr);
}




Last edited on May 31, 2016 at 4:29pm
May 31, 2016 at 4:47pm
If an array name is a pointer, you should be able to do this:
1
2
int arr[5];
arr++;
May 31, 2016 at 4:50pm
No you can't because that pointer is "chained" to the block of memory of the array. So, as you said in your previous post, I can't change the memory the pointer is pointing to.

However, how could I pass myArray to a function like byAddress() ?
Last edited on May 31, 2016 at 4:51pm
May 31, 2016 at 5:35pm
gedamial wrote:
"an array name isn't a pointer" sounds like "5 isn't an integer number" to me.

That appears to be the root of the problem. It is a lot closer to "3.15 isn't an integer number"

However, how could I pass myArray to a function like byAddress() ?

Depends on how you plan on using that int*** within "byAddress". The most likely direction would be
1
2
    int** p = myArray;
    byAddress(&p);

in C (not C++), that can be abbreviated to
byAddress(&(int**){myArray})


May 31, 2016 at 6:42pm
I feel very confused, since I've always treated arrays as pointers (not like real pointers, but sort of).

To lower the level of complexity, we can just focus on this example which results way more comprehensible:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void _val(int* p)
{
}

void _ref(int*& p)
{
}

void _addr(int** p)
{
}

void _RealAddr(int (&array)[5])
{
}

int main()
{
    	int hel[5];

	_val(hel);
	_ref(hel);
	_addr(&hel);
        _RealAddr(hel);
}


I admit it: I would have never done something like that, it doesn't make sense to me.

Let's examinate those functions:

1) In the first one the array decays into a pointer.

2) Illegal. We can't modify an array 'fake' pointer to point to something else.

3) Just illegal because I'm trying to add a new level of indirection on a not-pointer type (hel)

4) Legal. What's the difference with this function and number #1 ?
May 31, 2016 at 7:15pm
2) Illegal. We can't modify an array 'fake' pointer to point to something else.

It isn't fake, it's a very real pointer value constructed at the point of call as if by &hel[0]. You can modify it if you change that function to void _ref(int*&& p)

3) Just illegal because I'm trying to add a new level of indirection on a not-pointer type (hel)

you can add another level of indirection to any non-reference type. This is invalid because pointer to array and pointer to pointer are different types and there is no implicit conversion between them.

4) Legal. What's the difference with this function and number #1 ?

#1 takes a pointer by value, #4 takes an array by reference. What isn't different between them? Within #4, the type of the parameter is int[5]: you can check it with sizeof, you can apply std::begin, std::rank, std::extent, and other array-only functions, you can iterate it using a range-for loop. Can't do those things with a pointer.

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
void _RealAddr(int (&array)[5])
{
    for(int n: array) // in void _val(int* p), for(int n: p) won't compile
      std::cout << n << ' ';
}

int main()
{
    int hel[5] = {};
    _RealAddr(hel);
}
Last edited on May 31, 2016 at 7:22pm
May 31, 2016 at 8:46pm
1. I've been searching for documentation about that int*&&. How is this called? When is it used?

2. So you mean that all of these 3 functions:

1
2
3
void f(int arr[]);
void f(int arr[4]);
void f(int* arr);


convert the array to a pointer to its first element?

Meanwhile, this one

 
void _RealAddr(int (&array)[5])


just gives to the function the ARRAY without a conversion to pointer, right? But of course the size must be explicit.
May 31, 2016 at 9:05pm
1) rvalue reference. http://en.cppreference.com/w/cpp/language/reference#Rvalue_references

2) those are not actually 3 functions: those are three declarations for the same function with type void(int*). You can use one form for declaration and another for the matching definition. This parameter type rewriting for certain types is one of the most confusing things C invented (and C++ inherited). See http://en.cppreference.com/w/cpp/language/function#Parameter_list from "Because of these rules, the following function declarations declare exactly the same function:"

And yes, a function call to a function taking a pointer, however declared, with an argument of array type will cause a pointer to that array's first element to be created prior to function call (assuming such pointer would be same or convertible to the function's parameter)

But of course the size must be explicit.

It can be a deduced template parameter. There's about a dozen function templates in the C++ standard library that take arrays by reference.
Jun 1, 2016 at 1:03pm
I see rvalue references (&&) but I can't see the *&&
Jun 1, 2016 at 1:40pm
Much like int** is a pointer-to-pointer-to-int a int*&& is an (rvalue) reference-to-pointer-to-int.
Topic archived. No new replies allowed.