Passing the address of an array into a function in C++ - c++

I'm new to c++ and I am confused by the idea of passing a pointer array into a function.
Here's the function
void func(int *a){ a[0]=999;}
which is used by the main
int main()
{
int a[5]={1,2,3,4,5};
func(a);
std::cout << a[0]<< std::endl;
}
I understand this works perfectly as the name of an array is just the address of its first element.
Based on my understanding, &a refers to the address of the entire array while a refers to the address of the first element of the array, which should be identical to &a.
However, if I use
int main(){
int a[5]={1,2,3,4,5};
func(&a);
cout<< a[0]<< endl;
}
It returns the compiler error: no known conversion from 'int (*)[10]' to 'int *' for 1st argument; remove &
Could anyone please explain what's going on here?

Case 1
I understand this works perfectly as the name of an array is just the address of its first element.
The above statement is not technically correct. The first example worked because in many contexts(including when passing an array as an argument by value to a function) the array decays to a pointer to its first element due to type decay. This means, in example 1, when you passed a as an argument, there was an implicit array-to-pointer conversion which converted your array a of type int[5] to a pointer to its first element which is the type int*. Thus in example 1, the type of the argument and the type of the parameter matches and example 1 succeeded.
Note also that even though the decayed pointer and the address of the array both have the same value their types are different. The decayed pointer is of type int* while the &a is of type int (*)[5].
Case 2
Could anyone please explain what's going on here?
This is because a is of type int [5] which means &a is of type int (*)[5] which doesn't match with the type of the parameter int*.
That is, when you modified your code, you were passing &a which is of type int (*)[5] but the function parameter is of type int* and hence there is a mismatch in the type of the parameter and the argument you're passing and as there is no implicit conversion from a int (*)[5] to an int* we get the mentioned error saying:
no known conversion from 'int (*)[5]' to 'int *'

I understand this works perfectly as the name of an array is just the address of its first element.
This is a subtly wrong understanding. The array int[10] can implicitly convert to a pointer to (address of) the first element int*. This implicit conversion happens when you call the function that accepts int* parameter and passing int[10] argument.
&a refers to the address of the entire array while a refers to the address of the first element of the array, which should be identical to &a
The value of the pointers is identical. But the type of the pointers is different. One is a pointer to an array int (*)[10] and the other is a pointer to an element of such array int*. Neither type implicitly converts to the other, and hence you cannot pass &a as an argument to the function that expects int*. The type system of the language prevents you from making a mistake. This is explained by the compiler diagnostic message.

See a as a pointer. It points to the first element of the array, a[0] (or *a).
Then &a is the address of the pointer, which is unrelated to the above.
Note that (&a)[0] or *(&a) return the pointer, while (&a)[0][0] or (*(&a))[0] or *((&a)[0]) or **(&a) return the first element of the array.

Related

Is there any difference in passing &arr(address of entire block) or just passing name of array (address of first element)?

void func1(int* ptr)
{
printf("func1 :%d\n",++ptr);
}
int main()
{
int arr[4] = {0,1,2,3};
printf("Addr enter code here`f arr: %d\n",arr);
func1(arr); // first way:
func1(&arr); // second way: How this will be different from func1(arr).
}
The difference between &arr and arr is that the type of former is a pointer to an array int (*)[4], and the type of latter is an array int[4] which can decay to pointer to the first element int*. Both the pointer to the array, and the decayed pointer to the first element point to the same address because the first byte of the array is also the first byte of the first element of the array.
The difference between func1(&arr) and func1(arr) is that former is ill-formed because int (*)[4] doesn't implicitly convert to int*. In order to be able to call func1(&arr), you would have to accept a pointer of correct type:
void func1(int (*ptr)[4])
printf("func1 :%d\n",++ptr);
printf("Addr enter code here`f arr: %d\n",arr);
Both of these calls result in undefined behaviour. The type of the variadic argument must match the type required by the format specifier. You used the format specifier %d which requires the argument to be of type int (or similar). The type of the (decayed) argument here is int* which isn't int.
Arrays decay to pointer. Any of those pointers reference the same address in memory. The difference is in the type of this pointer.
int arr[5];
arr and &arr[0] have type pointer to int (int *)
&arr has type of pointer to five elements integer array ( int (*)[5])
We have a function named func1 that accepts a pointer to an int that is, its parameter is int*. Now lets see what is happening in each of the cases.
Case I
In this case we have func1(arr);
Here the variable arr is an array of size 4 with elements of type int (which is int [4]) but it decays to a pointer to the first element(which is int*) due to type decay.
So when you wrote,
func1(arr);
essentially you're passing an int* as an argument which matches with the type of the function parameter.
Case II
In this case we have func1(&arr);
Here &arr means a pointer to an array of size 4 with elements of type int.(which is int (*)[4]).
Now when you wrote,
func1(&arr);
essentially you're passing an int (*)[4] as an argument to the function func1. But note that the parameter of func1 is of type int*. So there is mismatch in type of argument and parameter.
Now in the screenshot you posted, the program seems to be compiling and executed successfully. But note that since in the second case there is mismatch in argument and parmeter type you will get undefined behavior.
Undefined behavior means anything1 can happen including but not limited to the program giving your expected output. But never rely on the output of a program that has undefined behavior.
1For a more technically accurate definition of undefined behavior see this where it is mentioned that: there are no restrictions on the behavior of the program.

Can a pointer convert to an array during a function call?

Consider the following piece of code:
#include <iostream>
#include <typeinfo>
void use_pointer(int *ptr)
{
std::cout << typeid(ptr).name() << std::endl;
}
void use_array(int arr[])
{
std::cout << typeid(arr).name() << std::endl;
}
int main()
{
int *ptr = nullptr;
// std::cout << typeid(int *).name() << std::endl; // output: Pi
int arr[1];
// std::cout << typeid(int[]).name() << std::endl; // output: A_i
use_pointer(arr);
use_array(ptr);
}
Compiling this program using g++ 6.5.0 outputs:
$ g++ -std=c++11 arrays_2.cpp -Wall
$ ./a.out
Pi
Pi
Now, when calling use_pointer(arr) the array is being decayed to a pointer. (Is that correct? The word "decay" is new to me.)
And the C++ Standard says at [conv.array#1]:
An lvalue or rvalue of type “array of N T” or “array of unknown bound
of T” can be converted to a prvalue of type “pointer to T”. The
temporary materialization conversion ([conv.rval]) is applied. The
result is a pointer to the first element of the array.
I think I understand that my "array of int" converts to a "pointer to int". (Is that correct?)
Now, what happens exactly when calling use_array(ptr). Since the type of the parameter in this case is array of int, did the "pointer to int" convert to an "array of int"?
A pointer to the standard would be much appreciated.
Pointers are pointers, and arrays are arrays. However, arrays naturally decays to pointers to their first element. So when you pass the array arr to any of the function you have, it will decay to &arr[0].
Also note that when declaring function arguments, array-notation (like int arr[]) doesn't mean that it's an array, it's still translated by the compiler as a pointer (i.e. int* arr).
Regarding the decay from array to pointer, it can't happen the other way. Once you have a pointer, all you have is that pointer and the single element it points to.
Your code has little to do with pointer decay. Rather, it demonstrates type adjustment of function parameters.
When used as a type of a function parameter, T[] is being adjusted to T*. This is not a decay, not a conversion, it's a rewrite. The program is rewritten to use T*, as if no T[] was ever there in the source code.
To reiterate, there are no function parameters of type T[] in C++. When the programmer writes one, the compiler immediately substitutes T* and forgets that T[] was ever there. This is in contrast with normal arrays and their decay to pointers. There are most definitely arrays in C++ which are very different from pointers. They decay to pointers only in (many but not all) expressions. It's a one way street: a pointer never un-decays to an array
Can a pointer convert to an array during a function call?
No. See the last example when passing by reference.
An array type has a size (that includes all elements). When it decays into a pointer this size information is lost. So there is no way to convert a pointer back into an array (as you don't know how big the array is).
You probably noticed that array and pointer parameters are actually synonyms for each other to the compiler:
> cat b.cpp
void use_pointer(int *ptr)
{}
void use_pointer(int (arr)[])
{}
> g++ b.cpp
b.cpp:8:6: error: redefinition of 'use_pointer'
void use_pointer(int (arr)[])
^
b.cpp:4:6: note: previous definition is here
void use_pointer(int *ptr)
It does not matter to the compiler these are both the same thing.
So when you pass an array as a parameter it will usually decay into a pointer (for the address of the first element of the array).
Now you can pass an array by reference but the syntax is different.
void use_pointerref(int (&arr)[1]) // You have to specify the exact size
{} // And lace the ampersand in there.
But you will notice that a pointer will not bind to this function.
use_arrayref(arr);
use_arrayref(ptr);
> g++ b.cpp
b.cpp:31:5: error: no matching function for call to 'use_arrayref'
use_arrayref(ptr);
^~~~~~~~~~~~
b.cpp:14:6: note: candidate function not viable: no known conversion from
'int *' to 'int (&)[1]' for 1st argument
void use_arrayref(int (&arr)[1])
Some notes:
But let us think about that. C++ semantics are normally pass by value. So if you had succeeded (or should I say if the language allowed) in passing the array you would have made a copy of the array that would have been passed to the function. This is probably not desirable so apssing the pointer is an efficient way of passing by value (though you do . get a type change).
Note that the pass by reference is very limited as it must know the exact type. We usually get around the size issue by making it a template.
template<int S>
void use_pointerref(int (&arr)[S])
{
std::cout << typeid(arr).name() << " Size: " << S << std::endl;
}

What's the type difference between a, &a, and &a[0]?

#include <iostream>
void arrayFunctionA(int* p)
{
}
int main()
{
int a[3] = {7, 7, 7};
arrayFunctionA(a);
arrayFunctionA(&a[0]);
arrayFunctionA(&a);
return 0;
}
This will not compile, with an error on arrayFunctionA(&a);.
So, a and &a[0] evaluates as int*, but &a does not.
What is the distinction between these 3?
a is an array of 3 int type (int [3]).
A fixed array decays to a pointer to its first element, so in arrayFunctionA(a), a will decay into an int* pointer to it's first element.
&a[0] is the address of the array's first element, and is of type int *.
&a is the address of the array itself, and is of type int (*)[3] (pointer to an array of 3 int).
So, for a function with signature
void arrayFunctionA(int* p);
Passing a or &a[0] will serve the same purpose, while passing &a will result in an incompatibility between the passed argument value and the function parameter.
The first two examples are identical.
In the first one, a decays to a pointer and binds to the int* taken by the function.
In the second one, &a[0] is identical to &*(p + 0) which yields a int*.
The third example doesn't compile because applying the address-of operator (&) to an array yield a pointer to an array. In your example the result is a int(*arr)[3] which cannot bind to a int* so you get the compilation error.
Оn the first call arrayFunctionA(a); the actual address of a is being passed.
Оn the second call arrayFunctionA(&a[0]); the actual address of index 0 is being passed.
The first and second calls are identical. You both get the address of index 0 (remember that an array is also a pointer).
On the third call you are getting the pointer of a which is already a pointer.

C++ pointer vs array notation

When I declare a new array like this:
int foo[5]
Is foo really a pointer to the first element of the array? Would I be able to do this:
*(foo+2)
to access the third element of the array? Let's say I'm making a 2D array:
int foo[3][4]
Is foo now a int**?
No, 'foo' is an array type in both cases but when a pointer is expected in expression with 'foo', it's implicitly converted to one (which points to the array first element). All arrays have this behavior. In the case, as addition can be done via pointer types, but not with arrays, 'foo' is converted to 'int *'.
*(foo+2) // 'foo' is implicitly converted into 'int *', pointing to 'foo' first element
foo + 1 //same as above
But now you may ask, what are the properties of the 'array' type and why we should ever use it, instead of the implicit pointer to first element cast. The things is that they are not much. You can tell the size of an object with type array like this:
sizeof(foo) //returns the size which array 'foo' occupies
And get it's address by using the '&' operator:
&foo // '&foo' has type of 'int (*)[5]'
You can also create functions with parameters of 'array' reference (or pointer) type in order to accept only ones with specified size (which is not possible if they are just pointers and expect arrays passed to decay into such). Example:
void func(int (&)[5]);
void func1(int (*arg)[5]); // should be accessed by '*arg', allow the use of null-pointers
void func2(int *); //same as the misleading 'void func2(int [5])' or 'void func2(int [6])' etc.
int foo[5];
int foo1[6];
func(foo); // 'foo' type is ('int [5]') - ok
func1(&foo); // '&foo' type is ('int (*)[5]') - ok
func(foo1); // 'foo1' type is ('int [6]') - not allowed, param type is 'int (&)[5]' !
func1(&foo1); // '&foo1' type is ('int (*)[6]') - not allowed, param type is 'int (*)[5]' !
func2(foo); // 'foo' is implicitly converted to 'int *' - ok
func2(foo1); // 'foo1' is implicitly converted to 'int *' - ok
In the second case when the array is 2D - the same properties are applied. It's declaration means this: 'an array of 3 elements with type array of 4 elements with type int' So it's actually just an array of arrays and nothing more. It's implicit pointer to first element conversion is not of type 'int **' but instead of 'int (*)[4]', as each element of it is another array.
The declaration can be written this way too:
int (foo[3])[4];
Also note 'arrays' cannot be assigned, so they can't be passed by value or returned by functions. What I mean is:
int funcReturningArray()[2]; //not allowed
int funcAcceptingArray(int [2]); //just converted into pointer
int funcAcceptingArray(int *); //same as above
Although array parameters are syntactically accepted because of legacy reasons (or because something else ?), their real meaning is never tolerated and they are just 'adjusted' to pointers.
Note: The implicit conversion of array type to a pointer of it's first element is sometimes called 'Array to pointer decay'.
No arrays are not pointers but in expressions they are converted to rvalue pointer to their first elements.
So in this expression
*(foo+2)
at first foo is converted to rvalue pointer and then the pointer arithmetic is used.
For this array declaration
int foo[3][4];
name foo used in expressions is converted to rvalue pointer of type int ( * )[4]
The word rvalue means that for example you may not write ++foo
That is the compiler for the array name used in expressions creates a temporary object that is a poimnter to the first element of the array.
Take into account that if you are using for example operator sizeof with an array name then the last will not be converted to a pointer. So for your last array definition
sizeof( foo )
will be equivalent to
3 * 4 * sizeof( int )
while
sizeof( int ( * )[4] )
will return the size of the pointer itself.
And at last if you will apply operator & to an array name then you will get a pointer to the array itself. For example
int foo[3][4];
int ( *ptr_to_foo )[3][4] = &foo;

ELI5: What is the data type of `int *p[]`

I don't understand what the datatype of this is. If its a pointer or an array. Please explain in simple terms. To quote what was in the book-
If you want to pass an array of pointers into a function, you can use the same method that you use to pass other arrays—simply call the function with the array name without any indexes. For example, a function that can receive array x looks like this:
void display_array(int *q[])
{
int t;
for(t=0; t<10; t++)
printf("%d ", *q[t]);
}
Remember, q is not a pointer to integers, but rather a pointer to an array of pointers to
integers. Therefore you need to declare the parameter q as an array of integer pointers,
as just shown. You cannot declare q simply as an integer pointer because that is not
what it is.
cite: C++: The Complete Reference, 4th Edition by Herbert Schildt, Page 122-123
This is how it's built up:
int is the type "int".
int* is the type "pointer to int"
int* [] is the type "array (of unknown bound/length) of pointer to int"
int* p[] is the declaration of a variable or parameter named p of the type above.
... pointer to an array of pointers to integers
No it's not. q is the type int *[]. Which is an invalid (or possibly incomplete, depending on context) type in C++, and only valid in some places in C. Arrays must have a size.
The type int *[] is an (unsized) array of pointers to int. It is itself not a pointer.
The confusion probably comes from the fact that an array can decay to a pointer to its first element.
For example, lets say we have this array:
int a[20];
When plain a is used, it decays to a pointer to its first element: a is equal to &a[0].
int *p[]
// ^
p is
int *p[]
// ^^
p is an array of unspecified size (possibly illegal, depends on context)
int *p[]
// ^^^^^
p is an array of unspecified size of pointers to int
Meaning each element of p is a pointer:
int foobar = 42;
p[0] = NULL;
p[1] = &foobar;
I don't understand what the datatype of this is
If it's any comfort, neither does the author of the book you are reading.
Remember, q is not a pointer to integers, but rather a pointer to an array of pointers to integers.
This is bullschildt.
Before adjustment of parameters, q is an array of pointers to integers.
After adjustment of parameters, q is a pointer to the first element of an array of pointers to integers. Equivalent to int** q, a pointer to pointer to an int.
Nowhere is it "a pointer to an array of pointers to integers". That would have been int* (*q)[].
I would advise to stop reading that book.
The key here is that any array that is part of a parameter list of a function, gets adjusted ("decays") into a pointer to the first element. So it doesn't matter if you type int* q[666] or int* q[], either will be silently replaced by the compiler with int** "behind the lines".
This is actually the reason why we can write [] in a parameter list - normally an empty array would be an incomplete type that can't be used before completion elsewhere. But since parameters always get adjusted, they are never of array type, and it doesn't matter that the original type was incomplete.