C++ pointer vs array notation - c++

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;

Related

Passing the address of an array into a function in 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.

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.

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.

Cannot convert from 'T[N][2]' to 'T[][2]'

I have an API taking some options:
void init_api(const char* options[][2]);
I am allowed to pass a NULL pointer for no options, alternatively, an options array such as this can be passed:
const char* some_options[][2] = { {"opt1", "val1"},
{"opt2", "val2"},
{0,0}
};
This works without problems:
...
init_api(some_options);
... or ...
init_api(NULL);
...
However, this fails to compile:
const char* my_options[][2] = NULL; // error C2440: 'initializing' : cannot convert from 'int' to 'const char *[][2]'
if(...) {
my_options = some_options; // error C2440: '=' : cannot convert from 'const char *[4][2]' to 'const char *[][2]'
}
init_api(my_options); // no error here
What is going on here? Can someone explain this?
To declare an empty array of array of pointers to const char, you should use:
const char* my_options[][2] = {};
You need to declare a pointer to an array of pointers to const char instead. I recommend using a typedef to simplify the syntax.
typedef const char* array_of_two_cstring[2];
array_of_two_cstring* my_options = NULL;
if (...) {
my_options = some_options;
}
init_api(my_options);
In C++ (it is herited from C), array can be implicitly converted to pointer (only once though, that is char[] is compatible with char* but char[][] is compatible with char*[] but not `char**). However, the variable cannot be reassigned. So here you need to use a pointer instead of an array.
The init_api option accepts NULL as a parameter because for the compiler, its prototype is void init_api(char const* (*)[2]) (the first array degenerated into a pointer), and NULL is a valid pointer.
The compiler must know the array size.
If you omit the size of the array (ie: using []) you need to initialize the array with the definition, in order to let the compiler count how many items that array will contain.
Moreover you are assigning a pointer (NULL) to an array: const char *x[][2] is an array of two pointers to const char.
Edit:
In C++ (as in C), arrays can decay into pointers when you use them (with three exceptions which are not interesting here).
When you pass an array to a function expecting an array, what happens is that you actually pass a pointer to the array, since the array decays; you cannot pass an array by value in C or C++.
For this reason you can pass NULL to your function; the function parameter will be NULL, and if you try to access the array within your function (options[0]) your application will crash: you'll be dereferencing an invalid pointer.
You cannot however set your array variable to NULL, since it's not a pointer, it's an array: it will only decay when you'll use it in an expression.
const char* options[][2]
is an array of const char* pointers. You can't assign a pointer to an array.
A parameter declared as being an array of type T[N] or T[] becomes actually a parameter of type T*. Same is done for functions (a parameter declared as R(Params) becomes actually a parameter of type R(*)(Params...)).
Such transformation however is not done for other declarations. The reason it's done for function by-value parameters is that there is no way in C to actually copy an array directly (that is, to actually copy its contents) and it doesn't make sense to try and copy a function either, so such parameters are transformed in a way that conveys their purpose in a meaningful way.
So while you are initializing a pointer in the function parameter case, you are trying to initialize an array in the other case.

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.