Type of different array declarations - c++

What is the type of the array declarations below? Who can say clearly for me what value in case?
int main() {
int* arr[3];
int(*arr)[3];
int*(arr[3]);
}

You can use cdecl to help out:
int *arr[3] gives "declare arr as array 3 of pointer to int".
int (*arr)[3] gives "declare arr as pointer to array 3 of int"
int*(arr[3]) gives "declare arr as array 3 of pointer to int".
Also, of course these are not "real" declarations; there's no terminating semicolons and you can't have three variables called arr in the same scope.

It’s generally a good idea to know the language you use instead of relying on tools to decipher the source code, so I do not recommend using C declaration analyzers.
An easy way to remember the precedence is to remember the signature of main, namely
auto main( int argc, char* argv[] )
-> int
… where you know that argv is an array of pointers (or technically a pointer to the first element of such an array: the declaration decays to char** argv).
That means that given argv you can index it to get a pointer, argv[i], and then dereference the result to get a char, so that the equivalent parenthesized declaration is
char* (argv[])
The declaration
char (*b)[3]
means that by dereferencing b, i.e. *b, you get an array that can be indexed, (*b)[], and so b is a pointer to an array.
It’s not a pointer to the first item of such an array. It’s a pointer to the array itself. Hence adding 1 to b gives you a pointer to a following array of the same size.

Postfix operators such as [] and function call () have higher precedence than unary *, so a declaration like *a[N] will be parsed as *(a[N]), or "N-element array of pointer". To declare a "pointer to N-element array", you need to explicitly group the unary * with the array name, or (*a)[N].
Here's a handy chart.
T a[N] -- N-element array of T
T *a[N] -- N-element array of pointer to T
T (*a)[N] -- pointer to N-element array of T
T f() -- function returning T
T *f() -- function returning pointer to T
T (*f)() -- pointer to function returning T
You can combine the above into pretty complex declarations:
T (*a[N])() -- N-element array of pointers to functions returning T
T (*f())[N] -- Function returning pointer to N-element array of T
T (*(*f())[N])[M] -- Function returning pointer to N-element array of pointer
to M-element array of T
When reading a hairy declaration, start with the leftmost identifier and work your way out, remembering the rules above:
f -- f
f() -- is a function returning ("()" has higher precedence than "*")
*f() -- a pointer to
(*f())[N] -- an N-element array of
*(*f())[N] -- pointers to
(*(*f())[N])[M] -- M-element arrays of
T (*(*f())[N])[M] -- T

Related

Double Pointers and a Parenthesis between them

I am learning c pointer and I follow c4learn as tutorial. In pointer to array of string section, which has following code:
char *arr[4] = {"C","C++","Java","VBA"};
char *(*ptr)[4] = &arr;
I didn't get what is
*(*ptr)[4]
? Wasn't it possible to use it like
**ptr
instead?
Update1:
Currently I am in the next section, function pointer and I saw again similar code:
void *(*ptr)();
char *(*ptr)[4]
is a pointer to a length 4 array of pointers to char (char*). Since arr is a length 4 array of char*, ptr can be made to point to arr by assigning it the address of arr, &arr.
void *(*ptr)();
Is a pointer to a parameterless function returning void*. For example
void* fun(); // function
void *(*ptr)(); // function pointer
p = fun; // function pointer points to function
C syntax can be quite confusing, so it may be easier to illustrate this with some examples. Note that whitespaces between T and ; make no difference.
T name[N]; // size N array of T
T * name[N]; // size N array of pointer to T
T (*name)[N]; // pointer to size N array of T
T ** name[N]; // size N array of pointer to pointer to T
T *(*name)[N]; // pointer to size N array of pointer to T
char *ar[4];
Declares ar as an array of four pointers to chars.
To declare a pointer, you take the declaration of something it can point to, and replace the variable name with (*some_other_name).
So char *(*ptr)[4]; declares ptr as a pointer to an array of four pointers to chars. Normally you can drop the brackets, but in this case, char **ptr[4]; would declare ptr as an array of four pointers to pointers to chars which is not what we want.
Similarly for a function pointer. void *fn() declares a function. void *(*ptr)() declares a pointer that could point to fn. void **ptr() would declare a function with a different return type.
Wasn't it possible to use it like **ptr instead?
Yes, assuming you mean like ptr2 below:
const char* arr[4] = {"C","C++","Java","VBA"};
const char* (*ptr)[4] = &arr;
const char** ptr2 = arr;
There is a difference though... with ptr the type still encodes the array length, so you can pass ptr but not ptr2 to functions like the one below:
template <size_t N>
void f(const char* (&arr)[N]) { ...can use N in implementation... }
Currently I am in the next section, function pointer and I saw again similar code: void *(*ptr)();
That creates a pointer to a function - taking no arguments - returning a void* (i.e. the address of an unspecified type of data, or nullptr).
char *(*ptr)[4] is an array pointer to an array of pointers.
With less obfuscated syntax: if you have a plain array int arr[4]; then you can have an array pointer to such an array by declaring int (*arr_ptr)[4].
So there are arrays, regular pointers and array pointers. Things get confusing because when you use the array name by itself, arr, it decays into a regular pointer to the first element. Similarly, if you have a regular pointer and let it point at the array, ptr = arr; it actually just points at the first element of the array.
Array pointers on the other hand, points at the "whole array". If you take sizeof(*arr_ptr) from the example above, you would get 4*sizeof(int), 4*4=16 bytes on a 32-bit machine.
It should be noted that an array pointer a mildly useful thing to have. If you are a beginner, you don't really need to waste your time trying to understand what this is. Array pointers are mainly there for language consistency reasons. The only real practical use for array pointers is pointer arithmetic on arrays-of-arrays, and dynamic allocation of multi-dimensional arrays.

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.

Advanced Pointer convoluted syntax

I was fidgeting with the pointers thingy.graduated from pointers -> array of pointers -> function pointers -> pointer to pointers..
Here's what i am stuck at...mostly the convoluted syntax.
Lets say i have an array of integers.
int arr[4] = {1,2,3..};
also i have array of pointers
int* ptr[4];
ptr[0] = arr;
here ptr[0] will point to 1
and ptr[1] will point to some other location
This works perfectly !!
Now considering above background i tried this.
char* crr[4] ={"C","C++","C#","F#"};
char** btr[4];
btr[0] = crr;
which works as pointer in oth element of btr is pointing to another pointer element in crr.
Then i tried this.
char* crr[4] ={"C","C++","Java","VBA"};
char** btr[4]= &crr; // Exception: cannot convert char* [4] * to char**[4]
but when i do this it works :O
char* crr[4] ={"C","C++","Java","VBA"};
char* (*btr)[4]= &crr;
i have not understood the last two scenarios. The use of brackets on RHS Pls explain.
char** btr[4]= &crr; // Exception: cannot convert char* [4] * to char**[4]
That's trying to initialise an array (of pointers to pointers) from a pointer, which you can't do. If you wanted to initialise the first element to point to crr (as in your first example), then you'd do
char** btr[4]= {crr};
The last example is declaring a pointer to an array (of pointers), and initialising it from a compatible pointer.
Note that your original array should be of const char *, not char *, since it contains pointers to (constant) string literals.
Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.
When you write
btr[0] = crr;
the expression crr has type "4-element array of char *"; since it is not the operand of a sizeof or & operator, it is converted to type "pointer to char *", or char **. This matches the type of btr[0].
However, when you write
char** btr[4]= &crr;
the expression crr is the operand of the & operator; thus, the type of the expression &crr is "pointer to 4-element array of char *", or char *(*)[4].
The parentheses are necessary because the postfix [] operator has higher precedence than the unary * operator; the expression *a[i] will always be parsed as *(a[i]). Thus,
T *a[N]; // a is an N-element array of pointer to T
T (*a)[N]; // a is a pointer to an N-element array of T
T *(*a)[N]; // a is a pointer to an N-element array of pointer to T
The same is true for pointers and functions:
T *f(); // f is a function returning a pointer to T
T (*f)(); // f is a pointer to a function returning T
T *(*f)(); // f is a pointer to a function returning pointer to T
Let's look at your definitions:
Array of size 4 of char*
char* crr[4] ={"C","C++","Java","VBA"};
Array of size 4 of char**
char** btr[4]
Pointer to an array of size 4 of char*
char* (*btr)[4]
Now you are trying to assign Array of size 4 of char* to Array of size 4 of char** and they are of different type, so you, obviously, have a error. Still there is another rule(which is dominated): you can't initialize array without curly braces, so array should be initialized with new data not to be a pointer to other array.
And you have absoulutely legal pointer type Pointer to an array of size 4 of char* to which your array address could be assigned

Is an array argument passed to a function not a constant pointer?

Consider the code:
void foo(char a[]){
a++; // works fine, gets compiled
//...
}
Now, consider this:
void foo(){
char a[50];
a++; // Compiler error
//...
}
I heard an array is equivalent to a constant pointer and can't be incremented as it is not a lvalue...
Then why does first code gets compiled, is it so because array arguments to functions are passed as a pointer, i.e. T[] is converted to T* for passing..
So, foo(a) passes a as a pointer.
But is it not back converted to T[] again because is declared as:
void foo(char a[]);
When you pass an array as an argument to a function, it decays to a pointer.
So the thing you increment inside the function body is a pointer, not an array.
This is a rather unfortunate feature inherited from the C language, with the rather yucky name of "decay." Since C once did not allow passing compound types by value, they decided to allow programmers to specify arrays as function parameter types, but only cosmetically. The array type decays to a pointer type, implementing a sort of pass-by-reference semantic different from the rest of the language. Ugly.
To recap (and others have already said this), the signature
void foo(char a[]); // asking for trouble
is unceremoniously mangled into
void foo(char *a);
… all for the sake of compatibility with ancient C code. Since you aren't writing ancient C code, you should not make use of this "feature."
However, you can cleanly pass a reference to an array. C++ requires that the size of the array be known:
void foo( char (&a)[ 50 ] );
Now a cannot be modified within the function (edit: its contents can, of course — you know what I mean), and only arrays of the right size may be passed. For everything else, pass a pointer or a higher-level type.
I heard an array is equivalent to a constant pointer
You can think of it that way, but they're not equivalent.
An array decays to a pointer when passed to a function, that's why inside the function it's valid.
Just because the signature is void foo(char a[]) doesn't make a an array.
Outside the function, it's just an array, and you can't do pointer arithmetics on it.
In C++, any function parameter of type "array of T" is adjusted to be "pointer to T". Try this code:
void foo(char a[]) {}
void foo(char* a) {} //error: redefinition
They are indeed the same function.
So, why can we pass an array argument to a function as a pointer parameter? Not because an array is equivalent to a constant pointer, but because an array type can be implicitly converted to an rvalue of pointer type. Also note the result of the conversion is an rvalue, that's why you can't apply the operator ++ to an array, you can only apply this operator to an lvalue.
I heard an array is equivalent to a constant pointer and can't be incremented as it is not a lvalue...
Almost.
An array expression is a non-modifiable lvalue; it may not be an operand to operators such as ++ or --, and it may not be the target of an assignment expression. This is not the same thing as a constant pointer (that is, a pointer declared as T * const).
An array expression will be replaced with a pointer expression whose value is the address of the first element of the array except when the array expression is an operand of the sizeof or unary & operators, or when the array expression is a string literal being used to initialize another array in a declaration.
When you call a function with an array argument, such as
int a[N];
...
foo(a);
the expression a is converted from type "N-element array of int" to "pointer to int" and this pointer value is what gets passed to foo; thus, the corresponding function prototype should be
void foo (int *arr) {...}
Note that in the context of a function parameter declaration, T a[] and T a[N] are identical to T *a; in all three cases, a is declared as a pointer to T. Within the function foo, the parameter arr is a pointer expression, which is a modifiable lvalue, and as such it may be assigned to and may be the operand of the ++ and -- operators.
Remember that all these conversions are on the array expression; that is, the array identifier or other expression that refers to the array object in memory. The array object (the chunk of memory holding the array values) is not converted.
When you pass an array a[] to a function, it passes the value of 'a', an address, into the function. so you can use it as a pointer in the function. If you declare an array in the function, 'a' is constant because you can't change its address in memory.

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.