Creating a pointer to a 2D array - arrays

I am new to C and spend some hours with arrays and pointers.
Now, I got a specific question I can't really answer by myself.
What are the two variables mat_ptr and ptr which I create in this example and why are they different?
To me it looks like each is an array of pointers storing the pointers to the beginning of the 3 rows of the matrix.
int matrix[3][3] = {{0,1,2},
{0,1,2},
{0,1,2}};
int (*mtr_ptr)[3] = matrix;
int *ptr[3];
for (int i=0; i< 3; i++)
{
ptr[i]=matrix[i];
}
I the end, I think ptr ist of type int ** but what exactly is ptr_ptr?
A pointer to an array of size 3 which is an array of arrays?

This code:
int matrix[5][5] = {{0,1,2,3},
{0,1,2,3},
{0,1,2,3}}
is missing a semicolon at the end. When that is fixed, it defines matrix to be a 5×5 array (formally an array of 5 arrays of 5 int) in which rows 0 to 2 are initialized with four values (0, 1, 2, and 3), leaving element 4 implicitly initialized to zero. Rows 3 to 4 are implicitly initialized to zero.
This code:
int (*mtr_ptr)[3] = matrix;
defines mtr_ptr to be a pointer to an array of 3 int and attempts to initialize it with matrix. Since matrix is an array, it will be automatically converted to a pointer to its first element, matrix[0]. Thus, we have a pointer to an array of 5 int. This is not a proper type to initialize a pointer to an array of 3 int, so the compiler will complain.
If matrix were defined as int matrix[5][3] or int matrix[3][3], and the then-excess initializers were removed, then the types in int (*mtr_ptr)[3] = matrix; would match, and the compiler would not complain.
This code:
int *ptr[3];
for (int i=0; i< 3; i++)
{
ptr[i]=matrix[i];
}
defines an array of 3 pointers to int and assigns them values from matrix[i]. Since each matrix[i] is an array, it will be converted to a pointer to its first element, matrix[i][0]. So each ptr[i] will be assigned to point to matrix[i][0].

First things first. There is a semi-colon missing from the definition of matrix, corrected below:
int matrix[5][5] = {{0,1,2,3},
{0,1,2,3},
{0,1,2,3}};
Regarding the definition of mtr_ptr below:
int (*mtr_ptr)[3] = matrix;
mtr_ptr is declared as a pointer to an array length 3 of ints. However, it is being initialized with an incompatible pointer. matrix is an array length 5 of array length 5 of int. In the assignment expression above, matrix is converted to a pointer to the first element. The elements of matrix have type array length 5 of int, so matrix is converted to a pointer to array length 5 of int with value equivalent to &matrix[0]. Therefore, mtr_ptr should be defined as:
int (*mtr_ptr)[5] = matrix;
or equivalently:
int (*mtr_ptr)[5] = &matrix[0];
Regarding the code below:
int *ptr[3];
for (int i=0; i< 3; i++)
{
ptr[i]=matrix[i];
}
ptr is defined as an array length 3 of pointers to int. matrix[i] is an array length 5 of int. In the assignment expression, it is converted to a pointer to its first element. The element type is int, so matrix[i] is converted to a pointer to int equivalent to &matrix[i][0]. Therefore, the assignement ptr[i]=matrix[i]; is equivalent to ptr[i]=&matrix[i][0];.

These declarations
int matrix[5][5] = {{0,1,2,3},
{0,1,2,3},
{0,1,2,3}}
int (*mtr_ptr)[3] = matrix;
do not make a sense. It seems you mean at least the following declarations
int matrix[3][4] = {{0,1,2,3},
{0,1,2,3},
{0,1,2,3}};
int (*mtr_ptr)[4] = matrix;
According to the C Standard (6.3.2.1 Lvalues, arrays, and function designators)
3 Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array
object has register storage class, the behavior is undefined.
So the array matrix used in expressions with rare exceptions is converted to pointer to its initial element of the type int ( * )[4].
Thus in this declaration
int (*mtr_ptr)[4] = matrix;
the pointer mtr_ptr is initialized by the address of the first "row" of the array matrix.
On the other hand, the expression matrix[i] is the i-th element of the array matrix that has the type int[4]. Such an array used in expression is in turn converted to pointer to its first element of the type int *.
In this line
int *ptr[3];
there is declared an array of three elements of the type int *.
And in this loop
for (int i=0; i< 3; i++)
{
ptr[i]=;
}
each element of the array of pointers is assigned by the address of the first element of the corresponding "row" of the array matrix that ("row") used in the expression matrix[i] is implicitly converted to pointer.

Unless it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character 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.
In the line
int (*mtr_ptr)[3] = matrix;
the expression matrix has type "5-element array of 5-element array of int". Since matrix is not the operand of the sizeof or unary & operators, it "decays" to type "pointer to 5-element array of int", or int (*)[5], and its value is the address of the first element of matrix (same as &matrix[0]).
int (*)[5] and int (*)[3] are different, incompatible types, so the compiler should yak at you on that initializer. You should declare mtr_ptr as
int (*mtr_ptr)[5] = matrix;
Remember that mtr_ptr is a pointer, not an array - it stores a single value.
That single value has type int (*)[5].
By contrast, the expression matrix[i] has type "5-element array of int"; by the same rule above, that expression "decays" to type "pointer to int", or int *. Since each element of your ptr array is storing an int *, you declare it as
int *ptr[3];
In this case ptr is an array, and each element simply stores an int *. That pointer may be the address of the first element matrix[i], or it may be a pointer to a single int object that isn't part of a larger array, or it may be something else:
ptr[0] = matrix[0];
ptr[1] = &i;
...
The size of ptr does not depend on the size of matrix beyond whether or not you want to store the address of all rows or matrix or just a subset.

Related

C treating array as pointer

In C I learnt that the name of array is a pointer to the first index, but why I can't write the following:
int mat[5][4];
*mat=1;
In C I learnt that the name of array is a pointer to the first index
This is not exactly so.
According to the C Standard (6.3.2.1 Lvalues, arrays, and function designators)
3 Except when it is the operand of the sizeof operator or the unary &
operator, or is a string literal used to initialize an array, an
expression that has type ‘‘array of type’’ is converted to an
expression with type ‘‘pointer to type’’ that points to the initial
element of the array object and is not an lvalue. If the array object
has register storage class, the behavior is undefined.
This declaration
int mat[5][4];
declares a two-dimensional array. So the array designator used in expressions (with rare exceptions listed in the quote) is implicitly converted to pointer to its first element that is to pointer of the type int ( * )[4].
Dereferencing the pointer like in this statement
*mat=1;
you will get an array lvalue of the type int[4]. And as a result you are trying to assign a one-dimensional array with the scalar integer value 1. But arrays do not have the assignment operator. They are non-modifiable lvalues.
Instead you could write for example
**mat = 1;
In this case the expression **mat yields a scalar lvalue of the type int (the first scalar element of the type int of the first "row"/element of the array m).
Here is a demonstrative program.
#include <stdio.h>
int main(void)
{
int mat[5][4];
**mat = 1;
printf( "**mat is equal to %d and equivalent to mat[0][0] that is also equal to %d\n",
**mat, mat[0][0] );
}
Its output is
**mat is equal to 1 and equivalent to mat[0][0] that is also equal to 1
In fact all these expressions used in the printf calls
printf( "%d\n", **mat );
printf( "%d\n", ( *mat )[0] );
printf( "%d\n", *mat[0] );
printf( "%d\n", mat[0][0] );
yield the same element of the array.
2D arrays are essentially 1D arrays of 1D arrays. *mat is equivalent to mat[0] and gives you the first element of the array, which is itself a 1D array (int[4]).
You can't assign a number to an array.
name of array is a pointer to the first index
I often see this explanation, but the difference between "array" and "name of array" is moot.
A better explanation is that an array can be automatically converted to the pointer to its first element.
You can use the array name as a pointer to the first element of your array (even though there are more elegant solutions).
In your case the first element of your array is another array, so *mat has the type int[4] (int array with 4 elements) and you can't assign 1 (integer) to it.
If you want to set the first integer of your two dimensional array, you could use
**mat = 1;
But I would highly recommend using explicit indexes like
mat[0][0] = 1;

Why pointer of type int (*ptr)[10] requires address of the array and not just array itself

Array returns pointer to the first element. I have simple code as below:
int a[10] = {1,2,3};//Filled three elements
int (*ptr)[10];//pointer to an array of 10 ints
ptr = a;
I am getting below warning.
warning: assignment from incompatible pointer type
If i modify the pointer definition as shown below then there is no warning
ptr = &a;
Why does the first assignment results in warning? Does &a returns integer pointer to array of 10 elements? Pls help to understand. Thanks
Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.
Except for the sizeof, & and alignof operator, arrays decay to pointers.
int a[10] = {1,2,3};//Filled three elements
int (*ptr)[10];//pointer to an array of 10 ints
ptr = a;
In the assignment ptr = a, a decays to a pointer to int (int *), which points to the first element.
int a[10] = {1,2,3};//Filled three elements
int (*ptr)[10];//pointer to an array of 10 ints
ptr = &a;
Here &a is of type int (*)[10], which is a pointer to an array of 10 ints and has the same type as ptr. It is the proper way to assign a pointer to an array.
Also note that the value itself is the same for a and &a, the only difference is the type.

what is the difference between these two pointer declaration

Here I have a 2D array. I want to declare a pointer to point to the first row of the array. First I did something like this
#include <stdio.h>
int main ()
{
int arr[2][2]={{6,2},{3,4}};
int *ptr=arr;
printf("%d",*(ptr+0));
}
First row of the 2D array is also a array. Similar type of pointer declaration for a 1D array don't give any warning. Why am I getting warning during compilation.
[Warning] initialization from incompatible pointer type [enabled by
default]
The following didn't give any warning
int main ()
{
int arr[2][2]={{6,2},{3,4}};
int (*ptr)[2]=arr;
printf("%d",*(ptr+0));
}
Except when it is the operand of the sizeof or unary & operators, 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.
In the line
int *ptr=arr;
the expression arr has type "2-element array of 2-element array of int" (IOW, T is "2-element array of int"). Since the expression arr is not the operand of either the sizeof or unary & operators, it is converted ("decays") to an expression of type "pointer to 2-element array of int", or int (*)[2], and the value of the expression is the address of the first element of the array.
This is why you got the warning for the line above, but not for
int (*ptr)[2]=arr;
since in that case the types match correctly.
Given the declaration
int arr[2][2]={{6,2},{3,4}};
the following are all true:
Expression Type Decays To Equivalent Value
---------- ---- --------- ----------------
arr int [2][2] int (*)[2] &arr[0][0]
&arr int (*)[2][2] &arr[0][0]
*arr int [2] int * &arr[0][0]
arr[i] int [2] int * &arr[i][0]
&arr[i] int (*)[2] &arr[i][0]
*arr[i] int arr[i][0]
arr[i][j] int
&arr[i][j] int *
So arr, &arr, arr[0], &arr[0], and &arr[0][0] all evaluate to the same value (the address of the first element of an array is the same as the address of the array itself), but the types are all different.
A very important point I want to get across (especially because it's shown up in a couple of other answers) is that array objects do not store any pointer values. If you looked at the contents of arr in memory, you'd see something like the following:
+---+
arr: | 6 | arr[0][0]
+---+
| 2 | arr[0][1]
+---+
| 3 | arr[1][0]
+---+
| 4 | arr[1][1]
+---+
No storage is set aside anywhere for any pointers. When your code is compiled, any expressions that refer to arrays are replaced with pointers to the first element of the array.
arr is an array of arrays of integers while ptr is a pointer to an integer. In an array of arrays you need two levels of indirection to reach an integer (arr[0][0]) while in a pointer you need just one (ptr[0]). You can assign an array of elements of a certain type to a pointer to an element of the same type, but not if the types are different which is the case here.
As you can see, they are two different things so the compiler does a good job on whining on your assignation.
2D arrays contain (as arrurri mentioned) a pointer which points to an array of pointers and each of those point to the actual elements. So, arr is a pointer to pointers and each indexed value of it (arr[i]) is a pointer to a row.
So, what you need to do is ptr=arr[0]; instead.

meaning of these formal parameters in C

I read these two different types of declaration of multidimensional array in formal parameters of a function in C.
int c[][10];
int (*c)[10];
How these two are same ? I am not getting the feel of it. Can anyone explain this with some example what the second one is trying to do?
Sorry if this has been previously asked..Please redirect me to the duplicate if any.
As a function argument, int* c and int c[] or even int c[7] are identical. See C FAQ.
The [10] part only tells the compiler how to do the arithmetic for accessing an element in the array - e.g. c[3][5]. Both these declarations are for a multidimensional array whose second dimension (as far as the compiler is concerned, inside this function) is of size 10.
Example:
#include <stdio.h>
int sum_all(int c[][2], int len) {
int res = 0, i ,j;
for (i=0; i < len; i++)
for (j=0; j < 2; j++)
res += c[i][j];
return res;
}
int main() {
int a[3][2] = { { 1, 2}, {3, 4}, {5, 6} };
printf("sum is %d\n", sum_all(a, 3));
return 0;
}
Note that the array is not checked to be of size 2, in this example. We could have passed a single dimensional array. The compiler does not care; we only told him how to access the elements in this array.
N1570:
6.7.6.3 Function declarators (including prototypes)
...
7 A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the
array type derivation. If the keyword static also appears within the [ and ] of the
array type derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at least as many
elements as specified by the size expression.
So, within the context of a function parameter declaration, T a[N] and T a[] are both equivalent to T *a; all three declare a as a pointer to T. In this particular case, T is "10-element array of int".
This goes hand-in-hand with the following:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.
Suppose you have an array declared as
int arr[5][10];
When you pass the array expression arr to a function, such as
foo( arr );
the array expression arr is converted from type "5-element array of 10-element array of int" to type "pointer to 10-element array of int", and that pointer value is what gets passed to the function. So your function prototype for foo would read as
void foo( int (*c)[10] )
or
void foo( int c[][10] )
or even
void foo( int c[5][10] )
but in all three cases, c is a pointer to an array, not a 2D array.
Consider what happens when you define int c[5][10] and then pass c to a routine. The array c will automatically be converted to a pointer to its first element, which is an array of 10 int.
So the language designers arranged it so that when you declare a parameter with c[][10], it is automatically adjusted to match the conversion that will happen. The compiler changes a parameter of type array of array of int to a parameter of type pointer to array of int.

Pointers to arrays problem in C

I have this function that takes a pointer of an array (in order to modify it from within the function)
int func_test(char *arr[]){
return 0;
}
int main(){
char var[3];
func_test(&var);
return 0;
}
When I try to compile this I get :
passing argument 1 of ‘func_test’ from incompatible pointer type
Why is this problem, and how I pass a pointer to that array in this case?
char * arr[] is not a pointer to an array; it is an array of pointers. Declarations in C are read first from the identifier towards the right, then from the identifier towards the left. So:
char * arr[];
// ^ arr is...
// ^ an array of...
// ^ pointers to...
// ^ char
A pointer to an array is a type (*varname)[], in your case, a char (*arr)[].
You are passing the address of a pointer. I think you want this:
int func_test(char arr[]){
arr[0] = 'a';//etc.
return 0;
}
int main(){
char var[3];
func_test(var);
return 0;
}
char var[3] is an array that holds 3 characters, not 3 pointers to characters - char *arr[] denotes an array that holds pointers to characters.
So you can go like this:
char *var[3];
func_test(var);
Note that the ampersand is not needed because array identifiers automatically decay to pointers of the corresponding type, in this case char **.
That's because the name of an array is already a pointer to it, so use func_test(var).
&var will have type char**
This is an array of pointers
char *arr[]
This is an address of a pointer
&var
So you are passing something different to what the function expects
C's treatment of arrays is a little confusing at first.
Except when it's an operand of either the sizeof or unary & operators, or when it's a string literal being used to initialize another array in a declaration, an expression with type "N-element array of T" will be implicitly converted to type "pointer to T", and its value will be the address of the first element in the array.
Assume the following declaration:
int x[10];
The type of the expression x is "10-element array of int". However, when that expression appears as, say, a parameter to a function:
foo(x);
the type of x is implicitly converted ("decays") to type "pointer to int". Thus, the declaration of foo needs to be
void foo(int *p);
foo receives an int *, not an int [10].
Note that this conversion also occurs for something like
i = x[0];
Again, since x isn't an operand of sizeof or &, its type is converted from "10-element array of int" to "pointer to int". This works because array subscripting is defined in terms of pointer arithmetic; the expression a[n] is equivalent to *(a+n).
So for your code, you'd write
int func_test(char *arr) { return (0); }
int main(void)
{
char var[3];
func_test(var); // no & operator
return 0;
}
Postfix operators like [] have higher precedence than unary operators like *, so a declaration like T *a[N] is interpreted as T *(a[N]), which declares an array of pointer to T. To declare a pointer to an array, you have to use parentheses to explicitly group the * operator with the array name, like T (*a)[N].
Here's a handy table of array declarations, expressions, and types:
Declaration: T a[N]; // a is an N-element array of T
Expression Type Decays To
---------- ---- ---------
a T [N] T *
&a T (*)[N]
a[i] T
&a[i] T *
Declaration: T *a[N]; // a is an N-element array of pointer to T
Expression Type Decays To
---------- ---- ---------
a T *[N] T **
&a T *(*)[N]
a[i] T *
*a[i] T
&a[i] T *
Declaration: T (*a)[N] // a is a pointer to an N-element array of T
Expression Type Decays To
---------- ---- ---------
a T (*)[N]
&a T (**)[N]
*a T [N] T *
(*a)[i] T

Resources