How to tell if a pointer is the pointer to an array? - c++

Consider this
template <class T>
inline constexpr bool found_to_be_array (T* specimen)
{
if constexpr (std::is_array_v<T>) {
return true;
}
else {
return false;
};
}
The question is in which context is this going to produce the reliable result?
Clarification: I can not change the footprint of this function. I have no answer, so let me post my findings here. Consider this:
int ia[]{ 1,2,3,4,5,6,7,8,9,0 };
int iam[3][3][3][3][3]{};
// returns false
auto is_array_1 = found_to_be_array(ia);
// returns true
auto is_array_2 = found_to_be_array(iam);
I am still researching this, but for multidimensional arrays, found_to_be_array works.
https://godbolt.org/g/ij73Z4

No this approach will not work. The parameter specimen has already decayed to a pointer type (or it might even have been a pointer all along); the metaprogramming technique used by std::is_array does not trace back in some way to the caller.
And besides, you're testing (unintentionally?) T rather than T* - but changing to the latter will not work.

How to tell if a pointer is the pointer to array?
A pointer has type, which indicates what it points to. When it points to array, then the type of the pointer reflects this, for example:
int arr[2][2] = {{1,2},{3,4}};
auto x = &arr[0];
x in this case has the type of int(*)[2]- a pointer to array of 2 ints, in this case {1,2}. If you increase this pointer by 1 it will point to the next array of 2 ints, namely {3,4}. If you pass this pointer to your function, T will be deduced as int[2] and the result will be true;
In case of 1D array it is no different:
int arr[4] = {1,2,3,4};
auto x = &arr;
x type will be int(*)[4] and it will also work and the function will return true.
But if you pass array to your function and not the pointer, thus forcing it to decay to the pointer to its 1st element, the array information is lost, because the pointer is of type int now.
auto x = arr;
x here is of type int *, it is not pointing to array, it points to the first int, which is 1. If you increase it by one it will point to the next int, which is 2 and so on. If this was a pointer to the array, it would point to the next byte after array end if you increase it by 1. Passing this pointer to your function would return false, because int type is not an array.
So to answer to your question, you can tell that the pointer is a pointer to array, because this information would be supplied in the pointer type.
int iam[3][3][3][3][3]{};
// returns true
auto is_array_2 = found_to_be_array(iam);
iam decays to the pointer to its 1st element, basically you are testing if iam[0] is an array, which it is, and so it works.

Whether a non-null data-pointer points to an array-element, is always answered maybe.
Why, you ask?
The language explicitly allows you to treat any object as the single element of an array of one. Which is quite nifty when you need a sequence, want to copy a trivial object, or the like.
Thus, every pointer is one of:
a null-pointer,
a pointer to an element of an array,
a pointer to beyond an element of an array, or
invalid, meaning one of
wild, meaning never initialized, or
dangling, meaning the pointee's lifetime was ended.
And while you can differentiate between case 1 and 2 or 3, the rest would need analysis of potentially the complete history of the current run of the program.

Your requirement that you can not change the function is weird/unreasonable.
If you can not change the function signature you can not detect how argument was produced(from ptr or array) because that information is lost.

Related

difference between pointer to an array and pointer to the first element of an array

int (*arr)[5] means arr is a pointer-to-an-array of 5 integers. Now what exactly is this pointer?
Is it the same if I declare int arr[5] where arr is the pointer to the first element?
Is arr from both the examples are the same? If not, then what exactly is a pointer-to-an-array?
Theory
First off some theory (you can skip to the "Answers" section but I suggest you to read this as well):
int arr[5]
this is an array and "arr" is not the pointer to the first element of the array. Under specific circumstances (i.e. passing them as lvalues to a function) they decay into pointers: you lose the ability of calling sizeof on them.
Under normal circumstances an array is an array and a pointer is a pointer and they're two totally different things.
When dealing with a decayed pointer and the pointer to the array you wrote, they behave exactly the same but there's a caveat: an array of type T can decay into a pointer of type T, but only once (or one level-deep). The newly created decayed type cannot further decay into anything else.
This means that a bidimensional array like
int array1[2][2] = {{0, 1}, {2, 3}};
can't be passed to
void function1(int **a);
because it would imply a two-levels decaying and that's not allowed (you lose how elements of the array are laid out). The followings would instead work:
void function1(int a[][2]);
void function1(int a[2][2]);
In the case of a 1-dimensional array passed as lvalue to a function you can have it decayed into a simple pointer and in that case you can use it as you would with any other pointer.
Answers
Answering your questions:
int (*arr)[5]
this is a pointer to an array and you can think of the "being an array of 5 integers" as being its type, i.e. you can't use it to point to an array of 3 integers.
int arr[5]
this is an array and will always behave as an array except when you pass it as an lvalue
int* ptrToArr = arr;
in that case the array decays (with all the exceptions above I cited) and you get a pointer and you can use it as you want.
And: no, they're not equal otherwise something like this would be allowed
int (*arr)[5]
int* ptrToArr = arr; // NOT ALLOWED
Error cannot convert ‘int (*)[5]’ to ‘int*’ in initialization
they're both pointers but the difference is in their type.
At runtime, a pointer is a "just a pointer" regardless of what it points to, the difference is a semantic one; pointer-to-array conveys a different meaning (to the compiler) compared with pointer-to-element
When dealing with a pointer-to-array, you are pointing to an array of a specified size - and the compiler will ensure that you can only point-to an array of that size.
i.e. this code will compile
int theArray[5];
int (*ptrToArray)[5];
ptrToArray = &theArray; // OK
but this will break:
int anotherArray[10];
int (*ptrToArray)[5];
ptrToArray = &anotherArray; // ERROR!
When dealing with a pointer-to-element, you may point to any object in memory with a matching type. (It doesn't necessarily even need to be in an array; the compiler will not make any assumptions or restrict you in any way)
i.e.
int theArray[5];
int* ptrToElement = &theArray[0]; // OK - Pointer-to element 0
and..
int anotherArray[10];
int* ptrToElement = &anotherArray[0]; // Also OK!
In summary, the data type int* does not imply any knowledge of an array, however the data type int (*)[5] implies an array, which must contain exactly 5 elements.
A pointer to an array is a pointer to an array of a certain type. The type includes the type of the elements, as well as the size. You cannot assign an array of a different type to it:
int (*arr)[5];
int a[5];
arr = &a; // OK
int b[42];
arr = &b; // ERROR: b is not of type int[5].
A pointer to the first element of an array can point to the beginning of any array with the right type of element (in fact, it can point to any element in the array):
int* arr;
int a[5];
arr = &a[0]; // OK
int b[42];
arr = &b[0]; // OK
arr = &b[9]; // OK
Note that in C and C++, arrays decay to pointers to the type of their elements in certain contexts. This is why it is possible to do this:
int* arr;
int a[5];
arr = a; // OK, a decays to int*, points to &a[0]
Here, the type of arr (int*) is not the same as that of a (int[5]), but a decays to an int* pointing to its first element, making the assignment legal.
Pointer to array and pointer to first element of array both are different. In case of int (*arr)[5], arr is pointer to chunk of memory of 5 int. Dereferencing arr will give the entire row. In case of int arr[5], arr decays to pointer to first element. Dereferencing arr will give the first element.
In both cases starting address is same but both the pointers are of different type.
Is it the same if i declare int arr[5] where arr is the pointer to the first element? is arr from both example are same? if not, then what exactly is a pointer to an array?
No. To understand this see the diagram for the function1:
void f(void) {
int matrix[4][2] = { {0,1}, {2,3}, {4,5}, {6,7} };
char s[] = "abc";
int i = 123;
int *p1 = &matrix[0][0];
int (*p2)[2] = &matrix[0];
int (*p3)[4][2] = &matrix;
/* code goes here */
}
All three pointers certainly allow you to locate the 0 in matrix[0][0], and if you convert these pointers to ‘byte addresses’ and print them out with a %p directive in printf(), all three are quite likely to produce the same output (on a typical modern computer). But the int * pointer, p1, points only to a single int, as circled in black. The red pointer, p2, whose type is int (*)[2], points to two ints, and the blue pointer -- the one that points to the entire matrix -- really does point to the entire matrix.
These differences affect the results of both pointer arithmetic and the unary * (indirection) operator. Since p1 points to a single int, p1 + 1 moves forward by a single int. The black circle1 is only as big as one int, and *(p1 + 1) is just the next int, whose value is 1. Likewise, sizeof *p1 is just sizeof(int) (probably 4).
Since p2 points to an entire ‘array 2 of int’, however, p2 + 1 will move forward by one such array. The result would be a pointer pointing to a red circle going around the {2,3} pair. Since the result of an indirection operator is an object, *(p2 + 1) is that entire array object, which may fall under The Rule. If it does fall under The Rule, the object will become instead a pointer to its first element, i.e., the int currently holding 2. If it does not fall under The Rule -- for instance, in sizeof *(p2 + 1), which puts the object in object context -- it will remain the entire array object. This means that sizeof *(p2 + 1) (and sizeof *p2 as well, of course) is sizeof(int[2]) (probably 8).
1 Above content has been taken from More Words about Arrays and Pointers.
The address of the whole array, and the address of the first element, are defined to be the same, since arrays in C++ (and C) have no intrinsic padding besides that of the constituent objects.
However, the types of these pointers are different. Until you perform some kind of typecast, comparing an int * to an int (*)[5] is apples to oranges.
If you declare arr[5], then arr is not a pointer to the first element. It is the array object. You can observe this as sizeof( arr ) will be equal to 5 * sizeof (int). An array object implicitly converts to a pointer to its first element.
A pointer to an array does not implicitly convert to anything, which may be the other cause of your confusion.
If you write int arr[5], you are creating an array of five int on the stack. This takes up size equal to the size of five ints.
If you write int (*arr)[5], you are creating a pointer to an array of five int on the stack. This takes up size equal to the size of a pointer.
If it is not clear from the above, the pointer has separate storage from the array, and can point at anything, but the array name cannot be assigned to point at something else.
See my answer here for more details.

Trying to understand "pointer to member"

I'm trying to understand how "pointer to member" works but not everything is clear for me.
Here is an example class:
class T
{
public:
int a;
int b[10];
void fun(){}
};
The following code ilustrate the problem and contains questions:
void fun(){};
void main()
{
T obj;
int local;
int arr[10];
int arrArr[10][10];
int *p = &local; // "standard" pointer
int T::*p = &T::a; // "pointer to member" + "T::" , that is clear
void (*pF)() = fun; //here also everything is clear
void (T::*pF)() = T::fun;
//or
void (T::*pF)() = &T::fun;
int *pA = arr; // ok
int T::*pA = T::b; // error
int (T::*pA)[10] = T::b; // error
int (T::*pA)[10] = &T::b; //works;
//1. Why "&" is needed for "T::b" ? For "standard" pointer an array name is the representation of the
// address of the first element of the array.
//2. Why "&" is not needed for the pointer to member function ? For "standard" pointer a function name
// is the representation of the function address, so we can write &funName or just funName when assigning to the pointer.
// That's rule works there.
//3. Why the above pointer declaration looks like the following pointer declaration ?:
int (*pAA)[10] = arrArr; // Here a pointer is set to the array of arrays not to the array.
system("pause");
}
Why "&" is needed for "T::b" ?
Because the standard requires it. This is to distinguish it from accessing a static class member.
From a standard draft n3337, paragraph 5.3.1/4, emphasis mine:
A pointer to member is only formed when an explicit & is used and its operand is a qualified-id not enclosed
in parentheses. [Note: that is, the expression &(qualified-id), where the qualified-id is enclosed in
parentheses, does not form an expression of type “pointer to member.” Neither does qualified-id, because
there is no implicit conversion from a qualified-id for a non-static member function to the type “pointer to
member function” as there is from an lvalue of function type to the type “pointer to function” (4.3). Nor is
&unqualified-id a pointer to member, even within the scope of the unqualified-id’s class. — end note]
For "standard" pointer an array name is the representation of the address of the first element of the array.
Not really. An array automatically converts to a pointer to first element, where required. The name of an array is an array, period.
Why "&" is not needed for the pointer to member function ?
It is needed. If your compiler allows it, it's got a bug. See the standardese above.
For "standard" pointer a function name is the representation of the function address, so we can write &funName or just funName when assigning to the pointer.
The same thing aplies here as for arrays. There's an automatic conversion but otherwise a function has got a function type.
Consider:
#include <iostream>
template<typename T, size_t N>
void foo(T (&)[N]) { std::cout << "array\n"; }
template<typename T>
void foo(T*) { std::cout << "pointer\n"; }
int main()
{
int a[5];
foo(a);
}
Output is array.
Likewise for functions pointers:
#include <iostream>
template<typename T>
struct X;
template<typename T, typename U>
struct X<T(U)> {
void foo() { std::cout << "function\n"; }
};
template<typename T, typename U>
struct X<T(*)(U)> {
void foo() { std::cout << "function pointer\n"; }
};
void bar(int) {}
int main()
{
X<decltype(bar)> x;
x.foo();
}
Output is function.
And a clarification about this, because I'm not sure what exactly your comment is meant to say:
int arrArr[10][10];
int (*pAA)[10] = arrArr; // Here a pointer is set to the array of arrays not to the array.
Again, array-to-pointer conversion. Note that the elements of arrArr are int[10]s. pAA points to the first element of arrArr which is an array of 10 ints located at &arrArr[0]. If you increment pAA it'll be equal to &arrArr[1] (so naming it pA would be more appropriate).
If you wanted a pointer to arrArr as a whole, you need to say:
int (*pAA)[10][10] = &arrArr;
Incrementing pAA will now take you just past the end of arrArr, that's 100 ints away.
I think the simplest thing is to forget about the class members for a moment, and recap pointers and decay.
int local;
int array[10];
int *p = &local; // "standard" pointer to int
There is a tendency for people to say that a "decayed pointer" is the same as a pointer to the array. But there is an important difference between arr and &arr. The former does not decay into the latter
int (*p_array_standard)[10] = &arr;
If you do &arr, you get a pointer to an array-of-10-ints. This is different from a pointer to an array-of-9-ints. And it's different from a pointer-to-int. sizeof(*p_array_standard) == 10 * sizeof(int).
If you want a pointer to the first element, i.e. a pointer to an int, with sizeof(*p) == sizeof(int)), then you can do:
int *p_standard = &(arr[0);
Everything so far is based on standard/explicit pointers.
There is a special rule in C which allows you to replace &(arr[0]) with arr. You can initialize an int* with &(arr[0]) or with arr. But if you actually want a pointer-to-array, you must do int (*p_array_standard)[10] = &arr;
I think the decaying could almost be dismissed as a piece of syntactic sugar. The decaying doesn't change the meaning of any existing code. It simply allows code that would otherwise be illegal to become legal.
int *p = arr; // assigning a pointer with an array. Why should that work?
// It works, but only because of a special dispensation.
When an array decays, it decays to a pointer to a single element int [10] -> int*. It does not decay to a pointer to the array, that would be int (*p)[10].
Now, we can look at this line from your question:
int (T::*pA3)[10] = T::b; // error
Again, the class member is not relevant to understanding why this failed. The type on the left is a pointer-to-array-of-ints, not a pointer-to-int. Therefore, as we said earlier, decaying is not relevant and you need & to get the pointer-to-array-of-ints type.
A better question would be to ask why this doesn't work (Update: I see now that you did have this in your question.)
int T::*pA3 = T::b;
The right hand side looks like an array, and the left hand side is a pointer to a single element int *, and therefore you could reasonably ask: Why doesn't decay work here?
To understand why decay is difficult here, let's "undo" the syntactic sugar, and replace T::b with &(T::b[0]).
int T::*pA3 = &(T::b[0]);
I think this is the question that you're interested in. We've removed the decaying in order to focus on the real issue. This line works with non-member objects, why doesn't it work with member objects?
The simple answer is that the standard doesn't require it. Pointer-decay is a piece of syntactic sugar, and they simply didn't specify that it must work in cases like this.
Pointers-to-members are basically a little fussier than other pointers. They must point directly at the 'raw' entity as it appears in the object.
(Sorry, I mean it should refer (indirectly) by encoding the offset between the start of the class and the location of this member. But I'm not very good at explaining this.)
They can't point to sub-objects, such as the first element of the array, or indeed the second element of the array.
Q: Now I have a question of my own. Could pointer decay be extended to work on member arrays like this? I think it makes some sense. I'm not the only one to think of this! See this discussion for more. It's possible, and I guess there's nothing stopping a compiler from implementing it as an extension. Subobjects, including array members, are at a fixed offset from the start of the class, so this is pretty logical.
The first thing to note is that arrays decay into pointers to the first element.
int T::*pA = T::b;
There are two issues here, or maybe one, or more than two... The first is the subexpression T::b. The b member variable is not static, and cannot be accessed with that syntax. For pointer to members you need to always use the address-of operator:
int T::*pa = &T::b; // still wrong
Now the problem is that the right hand side has type int (T::*)[10] that does not match the left hand side, and that will fail to compile. If you fix the type on the left you get:
int (T::*pa)[10] = &T::b;
Which is correct. The confusion might have risen by the fact that arrays tend to decay to the first element, so maybe the issue was with the previous expression: int *p = a; which is transformed by the compiler into the more explicit int *p = &a[0];. Arrays and functions have a tendency to decay, but no other element in the language does. And T::b is not an array.
Edit: I skipped the part about functions...
void (*pF)() = fun; //here also everything is clear
void (T::*pF)() = T::fun;
//or
void (T::*pF)() = &T::fun;
It might not be as clear as it seems. The statement void (T::*pf)() = T::fun; is illegal in C++, the compiler you use is accepting it for no good reason. The correct code is the last one: void (T::*pf)() = &T::fun;.
int (T::*pA)[10] = &T::b; //works;
3.Why the above pointer declaration looks like the following pointer declaration ?
int (*pAA)[10] = arrArr;
To understand this, we needn't confuse ourselves with member arrays, simple arrays are good enough. Say've we two
int a[5];
int a_of_a[10][5];
The first (left-most) dimension of the array decays and we get a pointer to the first element of the array, when we use just the array's name. E.g.
int *pa = a; // first element is an int for "a"
int (*pa_of_a)[5] = a_of_a; // first element is an array of 5 ints for "a_of_a"
So without using & operator on the array, when we assign its name to pointers, or pass it to function as arguments, it decays as explained and gives a pointer to its first element. However, when we use the & operator, the decay doesn't happen since we're asking for the address of the array and not using the array name as-is. Thus the pointer we get would be to the actual type of the array without any decay. E.g.
int (*paa) [5] = &a; // note the '&'
int (*paa_of_a) [10][5] = &a_of_a;
Now in your question the upper declaration is a pointer to an array's address without the decay (one dimension stays one dimension), while the lower declaration is a pointer to an array name with decay (two dimensions become one dimension). Thus both the pointers are to an array of same single dimension and look the same. In our example
int (*pa_of_a)[5]
int (*paa) [5]
notice that the types of these pointers are the same int (*) [5] although the value they point to are of different array's.
Why "&" is needed for "T::b" ?
Because that's how the language is specified. It was decided not to complicate the language with a member-to-pointer conversion just for the sake of saving a single character even though, for historical reasons, we have similar conversions for arrays and functions.
For "standard" pointer an array name is the representation of the address of the first element of the array.
No it isn't; it's convertible to a pointer to its first element due to an arcane conversion rule inherited from C. Unfortunately, that's given rise to a widespread (and wrong) belief that an array is a pointer. This kind of confusion is probably part of the reason for not introducing similar bizarre conversions for member pointers.
Why "&" is not needed for the pointer to member function ?
It is. However, your compiler accepts the incorrect void main(), so it may accept other broken code.
For "standard" pointer a function name is the representation of the function address, so we can write &funName or just funName when assigning to the pointer.
Again, the function name isn't a pointer; it's just convertible to one.
Why the above pointer declaration looks like the following pointer declaration ?
One is a pointer to an array, the other is a pointer to a member array. They are quite similar, and so look quite similar, apart from the difference which indicates that one's a member pointer and the other's a normal pointer.
Because T on it's own already has a well defined meaning: the type Class T. So things like T::b are logically used to mean members of Class T. To get the address of these members we need more syntax, namely &T::b. These factors don't come into play with free functions and arrays.
A pointer to a class or struct type points to an object in memory.
A pointer to a member of a class type actually points to an offset from the start of the object.
You can think of these kind of pointers as pointers to blocks of memory. These need an actual address and offset, hence the &.
A pointer to function points to the access point of the function in the assembly code. A member method in general is the same as a function that passes a this pointer as the first argument.
That's in crude nut shell the logic behind needing a & to get the address for members and object address in general.
void (*pF)() = fun; //here also everything is clear
It doesn't work because function fun is undefined
int T::*pA = T::b; // error
What is T::b? T::b is not static member. So you need specific object. Instead write
int *pA = &obj.b[0];
Similarly,
int (T::*pA)[10] = &T::b; //works;
It can be compiled. But it will not work as you expected. Make b static or call obj.b to get access to defined member of defined object. We can easily check this. Create conctructor for your class T
class T
{
public:
T() {
a = 444;
}
int a;
int b[10];
void fun(){}
};
On what value points pA ?
int T::*pA = &T::a;
*pA doesn't not point on variable with value 444, because no object has been created, no constructor has been called.

Differences in using array in main program or in functions sub-routines

I'v got some question about using array pointers in program.
When I use some array name (which is a const pointer to first array element)
char charTab[] = "ABCDEFGHIJKLMNOPRSTUWXYZ"; /* Basic data buffer */
char *charPtr = charTab; /* Assign */
charPtr += 3; /* It's ok, now we point 4th element */
charTab += 3; /* No, the lvalue required for '+' operand */
But when I create let's say the following function:
void CharTabMove(char *tabToMove, int noToMove);
With definition
void CharTabMove(char *tabToMove, int noToMove)
{
printf("-------IN FUNCTION---------\n");
printf("It's pointing to %c\n", *tabToMove);
tabToMove += noToMove;
printf("Now it's pointing to %c\n", *tabToMove);
printf("-------LEAVING FUNCTION---------\n");
fflush(stdout);
}
The function is allow to move this pointer along the array with no problem. Sure, after leaving the function the pointer will be still pointing to first element of charTab, but why the function is allowed to move the constant pointer?
Thanks in advice for response, I'm trying to explain that to my 11 yo nephew :)
---EDIT after couple of years ---
Ok, time pass and now I see why my question was not formulated accurately. I misguided you by using term const pointer referring to array name before and after passing it function. To rephrase, question boils down to:
Why this is not allowed:
char charTab[] = "ABCDEFGHIJKLMNOPRSTUWXYZ"; /* Basic data buffer */
charTab += 3; /* No, the lvalue required for '+' operand */
While this is allowed:
char charTab[] = "ABCDEFGHIJKLMNOPRSTUWXYZ"; /* Basic data buffer */
void CharTabMove(char *tabToMove, int noToMove)
{
tabToMove += noToMove;
}
CharTabMove(charTab)
And the answer is as I gathered and refined all of your answers, charTab acts almost like constant pointer to first element of array, but essentially is not an lvalue or a pointer statement, so arthmetic operations are not allowed. When passing it to function, this almost like constant pointer to first array element will be casted and passed by value as regular pointer, so operations can be executed on it.
There is one difference between an array name and a pointer that must be kept in mind. A pointer is a variable, so pa=a and pa++ are legal. But an array name is not a
variable; constructions like a=pa and a++ are illegal. here pa is pointer.
charPtr += 3; ==> this is allowed because this is pointer , pointer arithmetic is allowed and you can change the location of pointer.
charTab += 3; ==> here this is illegal.you should not change the position of array.
and
tabToMove ==> is character pointer.you can modify it.
First of all, the name of the array is not a constant pointer, it is just name of the array; the point is that an array in almost any context decays to a pointer to the first element of the array.
The array by itself cannot be incremented, but, once it decays to a pointer to the first element and is assigned to some pointer variable, it can be incremented (the second step is fundamental, since, to increment something, you must have an lvalue).
Now, functions cannot directly receive arrays as parameters (even if you write an array in the function signature it's implicitly interpreted as a pointer), so, when you pass an array to a function it surely decays to a pointer to its first element, and it's used to initialize the parameter of the function, which then can be incremented as you wish: in facts, the parameter tabToMove is just a local variable of type char * which is initialized with the passed argument, i.e. the pointer to the first element of the array.
In other words, if you write
char foo[]="asdasdas";
CharTabMove(foo, 5);
is like you were doing
char foo[]="asdasdas";
char *tabToMove=foo;
int noToMove=5;
CharTabMove(tabToMove, noToMove);
charTab is an array; charPtr and tabToMove are pointers. If you were able to change charTab you'd effectively lose track of where the beginning of the array is. If you change charPtr or tabToMove you don't have the same problem -- they're copies of the pointer to the beginning of the array.
but why the function is allowed to move the constant pointer?
It's not a constant pointer -- you've declared it as char *.
You cannot change a const pointer.
Actually, in the function, you just change a non-const pointer which just has the same value(passed by value when calling function) of your const point.

When Declaring a Reference to an Array of Ints, why must it be a reference to a const-pointer?

Note: I am using the g++ compiler (which is I hear is pretty good and supposed to be pretty close to the standard).
Let's say you have declared an array of ints:
int a[3] = { 4, 5, 6 };
Now let's say you really want to declare a reference to that array (nevermind why, other than the Bjarne says the language supports it).
Case 1 -- If you try:
int*& ra = a;
then the compiler balks and says:
"invalid initialization of non-const reference of type `int*&' from a temporary of type `int*'"
First things first, why is 'a' a temporary variable (i.e. doesn't it have a place in memory?)...
Anyway, fine, whenever I see a non-const error, I try to throw in a const...
Case 2 -- if you try:
int*const&rca = a; //wish I knew where the spaces should go (but my other post asking about this sort of protocol got a negative rank while many of the answers got ranked highly -- aha! there are stupid questions!)
Then everything is cool, it compiles, and you get a reference to the array.
Case 3 -- Now here is another thing that will compile:
int* justSomeIntPointer = a; //LINE 1
int*& rpa = justSomeIntPointer; //LINE 2
This also gives you a reference to the original array.
So here is my question: At what point does the name of a statically declared array
become a const-pointer? I seem to remember that the name of an array of ints is also a pointer-to-int, but I don't remember it ever being a const-pointer-to-int...
It seems like Case 1 fails because the reference declared (ra) is not to a const-pointer, which may mean that 'a' was already a const-pointer-to-int to begin with.
It seems like Case 2 works because the reference declared (rca) is already a const-pointer-to-int.
Case 3 also works, which is neat, but why? At what point does the assumed pointer-to-int (i.e. the array name 'a') become a const-pointer? Does it happen when you assign it to an int* (LINE 1), or does it happen when you assign that int* to a int*& (LINE 2)?
Hope this makes sense. Thanks.
int*& ra = a;
int* is a pointer type, not an array type. So that's why it won't bind to a, which has type int[3].
int* const& ra = a;
works, because it is equivalent to
int* const& ra = (int*)a;
That is, a temporary pointer is conceptually created on the right-hand side of the assignment and this temporary is then bound to ra. So in the end, this is no better than:
int* ra = a;
where ra is in fact a pointer to the first element of the array, not a reference to the array.
Declaring a reference to an array the easy way:
typedef int array_type[3];
array_type& ra = a;
The not-as-easy way:
int (&ra)[3] = a;
The C++11-easy way:
auto& ra = a;
At what point does the name of a statically declared array become a const-pointer? I seem to remember that the name of an array of ints is also a pointer-to-int, but I don't remember it ever being a const-pointer-to-int...
This is the right question to ask! If you understand when array-to-pointer decay happens, then you're safe. Simply put there are two things to consider:
decay happens when any kind of 'copying' is attempted (because C doesn't allow arrays to be copied directly)
decay is a kind of conversion and can happen anytime a conversion is allowed: when the types don't match
The first kind typically happen with templates. So given template<typename T> pass_by_value(T);, then pass_by_value(a) will actually pass an int*, because the array of type int[3] can't be copied in.
As for the second one, you've already seen it in action: this happens in your second case when int* const& can't bind to int[3], but can bind to a temporary int*, so the conversion happens.
The word "array" in C++ is spelled with brackets []. If you want to declare a something-array-something in C++, you have to have brackets in your declaration. If you write an asterisk * instead, you will get a pointer. Pointers and arrays are two different things.
This is a reference to an array:
int (&ra) [3] = a;
The very big mistake (also a very good interview question) that most people make is that they think the name of an array is equivalent to a pointer. That is NOT true. This mistake causes many bugs in C programs especially linking bugs, and they are very hard to debug. The diffrence is this: The name of the array, is a pointer the first element of a structure, the array. The type of the array name is not a pointertype however, but an arraytype. A pointer, on the other hand, is just a pointer to one thing with no other information. The type of a pointer is a pointertype. An arraytype has some other properties like it knows whether its on the stack or not; therefore, "temporary". The temporary error in your case comes from a check that prevents a temporary variable to be assigned to a reference. The const keyword turns that check off. A pointertype on the other hand has no notion of "temporary". Now suppose you want to trick the compiler and assign a reference to something that is in the stack. In that case, you need to make it a pointer. How?
int*& ra = &a[0];
in the above case you first get the value and using a &(address of operator) you make a pointerType. Now a pointertype has no information about whether its on the stack (a temporary variable) or not. This however, will make a reference to a pointer to the first element of the array. (Therefore just a pointer type, not an arraytype)
If you really want a reference to an array, then you should use the following:
int a[3] = { 4, 5, 6 };
int (&ra)[3] = a;
What you are trying to create with int *& is a reference to a pointer to an int. This is not the same type. And as you initialize the reference with a value that cannot change (the address of the array) you have to declare the pointer const (not the ints).
You have an array of ints :
int a[3] = { 4, 5, 6 };
Now, this line :
int*& ra = a;
creates a reference to a pointer. Since you create a temporary pointer (converted from the array a), the compiler complains, because the standard forbids assignment of temporaries to a reference.
So, to fix it, you need to create a pointer, and then assign it to a reference to a pointer :
int *pa = a;
int *& rpa = pa;
Constant references can hold reference to temporaries, but you already found that out.
What you asked (about reference to an array) - the most famous example about creating a reference to an array is this :
template< typename T, size_t N >
size_t ArraySize( T (&)[ N ] )
{
return N;
}
This function takes a reference to an array, and returns it's size.
At what point does the name of a statically declared array become a const-pointer? I seem to remember that the name of an array of ints is also a pointer-to-int, but I don't remember it ever being a const-pointer-to-int...
Because you written the values right there in the cpp file, that's why it's constant.
You can just use:
const int *pToArray = a;
or
const int *pToArray = (const int*)&a[0];
a is a temporary variable because you declared it on the stack and not on the heap using a malloc or a new.

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.