Access by pointer to multi-dimensional static arrays in C/C++ - c

I know, that static arrays are laid out contiguously in memory.
So, for example, int T[10][10] is basically stored the same way as int T[100].
I can access element of index i, j many ways, for example:
int T[10][10];
/*filling array*/
int i=3, j=7;
int x = T[i][j];
//EDIT - old and wrong: int * ptr = T;
int * ptr = &T[0][0];
int y = *(ptr + 10* i + j);
On the other hand, when I create dynamicaly allocated 2-dimensional array by myself:
int ** T;
T = malloc(10 * sizeof(int *));
for(i = 0; i < N; i++)
T[i] = malloc(10 * sizeof(int));
My array contains pointers to
It is obvious, that I can access element of this array by:
int i=3, j=7;
int x = *(*(T+i)+j);
And now my question: why and how does it work for static arrays?
Why does
int T[10][10];
/*filling array*/
int i=3, j=7;
int x = *(*(T+i)+j);
return good value to x, when this table doesn't contain pointers to arrays? *(*(T+i)) shouldn't have sense there in my opinion, end even if, it should return T[0][i], as T points at the first element of array. How does compiler interpret this, is * something other than dereference here? Enlighten me.

For starters:
int * ptr = T;
That's not going to actually work, at least without your compiler yelling at you. Very loudly. The correct way to do this is:
int * ptr = &t[0][0];
This point is actually very relevant to your question.
As you know, when used in an expression, an array gets decayed to a pointer. For example:
char foo[10];
bar(foo);
When used in an expression, like a parameter to a function, the array decays to a pointer to the underlying type. foo gets you a char *, here.
However, and this is the key point: the array only decays one level. If the array is a two-dimensional array, the array does not get decayed to the underlying value, an int in this case. The two dimensional array reference decays to a pointer to a one-dimensional array:
int T[10][10];
/*filling array*/
int i=3, j=7;
int x = *(*(T+i)+j);
The sequence of steps that occurs here:
T decays to a pointer to an array of 10 integers, or int (*)[10]
The addition of i advances the pointer by the given value. The pointer is advanced by the size of the element being pointed to. Since the pointer points to array of 10 integers, the pointer is advanced accordingly. If i was 2, the pointer is advanced by "two arrays of 10 integers", loosely speaking.
The * operator takes a "pointer to an array of 10 integers" and gives you "an array of 10 integers" as a result. In other words: from int (*)[10] to int [10].
Since the result is used in an expression, namely the left operand of + j, and the left operand is an array type, the array type decays to a "pointer to int".
j is added to the result, and dereferenced.

Why does
int T[10][10];
/*filling array*/
int i=3, j=7;
int x = *(*(T+i)+j);
return good value to x
The magic is all in *(*(T+3)+7) (I have converted to the literal values).
T is an array (of size 10) of arrays (of size 10) of int.
When T is used in an an expression it decays into a pointer to its first element, so it decays to "pointer to arrays (of size 10) of int".
Adding an integer to that pointer will advance to the fourth element of the array.
So T+3 is a pointer to an array of 10 ints, and specifically the fourth such array in T.
*(T+3) indirects through that pointer to give an l-value of type "array of 10 ints".
Ah-ha! That is another array being used in an expression - so it decays into a pointer to it's first element! (It wouldn't decay in sizeof, so sizeof(*(T+3)) would be typically 40.)
(*(T+3) + 7) just points at the eight element in the array, and ...
*(*(T+3) + 7) is an l-value of type int!

Related

How to create a pointer to a multi dimensional array in C

Before anyone closes this question because there's another related one, hear me out. I've already looked at the other question and I didn't get it. I would like to create a pointer to a multi dimensional array but I don't know how. I thought I was supposed to do it like this:
int test_arr[2][4];
int *ptr_array = test_arr;
But when I do that, I get a warning saying:
incompatible pointer types initializing 'int *' with an expression of type 'int [2][4]'
I have no idea what I'm doing wrong. Can someone help me please?
In order to do pointer arithmetic the pointer has to know the size of what it is pointing to.
int test_arr[2][4]; is equivalent to 2 elements of type int[4]. So whenever you add 1 to the pointer, it will jump the size of 4 integers. If you had int* it would increment the size of a single integer only.
Like said in the comments, what you want is: int (*ptr_array)[4] = test_arr;
I know, the syntax is a little weird, but that's how you do it.
You need to understand the basics.
[Below in post, read 1D as 1 dimension and 2D as 2 dimension]
First, lets take an example of pointer to 1D array:
Take an int type and a pointer pointing to it:
int x = 1;
int *px = &x;
The type of x is int and the type of &x is int * and type of px is int * i.e. it can hold the address of an int type variable. Hence the assignment is valid.
Take a 1D int array and a pointer pointing to it:
int arr[2] = {1,2};
int *parr = arr;
Note that the type of px and parr is same i.e. int * and assigning arr to parr is completely valid although the arr is a 1D array.
This is because of the fact that, in C, an array of type converted to pointer of type that points to the initial element of the array object but there are few exceptions to this rule and one of them is when used unary & operator.
The type of arr is int [2] but in the above statment (parr declare and initialisation statement) it is converted to pointer pointing to initial element of array which is an integer i.e. arr is converted to int * type in the int *parr = arr; statement. Hence, it is valid to assign arr to parr or to any variable of type int *.
As, I have mentioned that there are few exceptions to the rule (array of type converted to pointer of type....) and one of the exception is when used unary & operator. So type of &arr will be int (*)[2]. That means you can assign &arr to any variable of type int (*)[2].
int (*parr2)[2] = &arr;
i.e. parr2 is a pointer to an array of 2 integer.
The difference between this
int *parr = arr;
and
int (*parr2)[2] = &arr;
is that parr can hold address of any int pointer but parr2 can hold the address of any array of 2 integers. Both are pointing to same address but their types are different.
If you understood the above mentioned things completely then read below otherwise read the above part again.
Now lets come to 2D array.
Take a 2D array and a pointer pointing to it:
int arr[2][2];
int (*ptr)[2] = arr;
In the above ptr declare and initialisation statement, the type of arr is int (*)[2]. Why??
Recall the rule mentioned above - array of type converted to pointer of type that points to the initial element of the array object.
arr is array of 2 1D arrays of size 2 (2 int elements). So, it's first element is array of 2 int and, by rule, it will be converted to pointer to the initial element i.e. pointer to
1D array of 2 int which is int (*)[2].
Hence, you can assign arr to a pointer variable of type int (*)[2].
The type of &arr is int (*)[2][2], so this is also perfectly fine:
int (*ptr2)[2][2] = &arr;
So, there are two ways you can have pointer to 2D arr but remember that the types of ptr and ptr2 is different. This matters, for e.g., if you add 1 to pointer ptr it will be incremented by size of int [2] but if you add 1 to ptr2 it will be incremented by size of int [2][2].
How to access the array members using pointer:
In case of 1D array:
int arr[2] = {1,2};
int *parr = arr;
int (*parr2)[2] = &arr;
// access arr members via parr pointer
printf ("%d\n", parr[0]); // this will give value of first member of array arr
// access arr members via parr2 pointer
printf ("%d\n", (*parr2)[0]); // this will give value of first member of array arr
Similarly, in case of 2D array:
int test_arr[2][4];
int (*ptr_array)[4] = test_arr;
int (*ptr_array2)[2][4] = &test_arr;
// access test_arr members via ptr_array pointer
printf ("%d\n", ptr_array[0][0]); // this will give value of first member of array test_arr
// access test_arr members via ptr_array2 pointer
printf ("%d\n", (*ptr_array2)[0][0]); // this will give value of first member of array test_arr
Based on above explanation, try yourself creating multidimensional array (> 2D array) pointers and play with them.
Hope, above explanation resolves your all confusions related to pointer to a 2D array and you understood now why compiler is giving incompatible pointer types .... warning on statement int *ptr_array = test_arr;. It's all about type compatibility during initialisation/assignment.

The name of a 2-D array in C

I am learning pointers and arrays in C and Here is a question that confused me for a while:
So the name of a 1D int array is a constant pointer to int, which points to the first element in that array. So when we evaluate the name of a 1D array, we should get the address of the first element in the array.
For a 2D int array, the array name is a pointer to the first array of int. So what will be the value of a 2D int array's name? I think it should be the address of the first array in this 2D array. But how is the address of an array defined in C? Is it just the address of the first element in that array?
So the name of a 1D int array is a constant pointer to int
This is wrong, and it is often taught badly. An array is an array. Here is some code for analogy:
int x = 5;
double d = x + 1.2;
In the second line x is converted to double for purposes of addition. This does not change x which is still an int, the result of conversion is "temporary" and only exists until the addition is finished. The conversion is demanded by the conditions of the + operator that both arithmetic operands must be brought to a common type (double in this case).
In the array case, say we have char *p = arrayname + 1 , then arrayname is still an array. But it is converted to a temporary pointer so that the addition can occur (the + operator requires this, it can add a pointer and an integer). The temporary pointer points to the first element of the array, but it is not correct to say that the temporary pointer is the array.
Most operators invoke this conversion of an array to a temporary pointer, but some do not. So it is not correct to say that arrayname is a pointer because it may be used with an operator that does not convert the array to a pointer, e.g. sizeof arrayname.
The result of converting an array to a pointer is a pointer to the first element of that array. This is always true even if the first element is itself an array.
But how is the address of an array defined in C? Is it just the address of the first element in that array?
No. Every variable has an address, this applies to arrays and non-arrays. If you understand the address of an int then you also understand the address of a 2x2 array of char.
Let's clear up some things:
int a = 24;
The above is many things:
a declaration: we declare a variable named a of type int.
a definition: an object of type int is created.
an initialization: this object is initialized with the value 24
So let's recap: an object of type int is created with the value 24 and the variable a names it.
Now let's apply the same to the following:
int a1[3] = {0, 1, 2};
a declaration: we declare a variable named a1 of type int[3] (aka array of 3 integers).
a definition: an object of type "array of 3 integers" is created
an initialization: the object is initialized with {0, 1, 2}
The variable a1 names that object.
So the name of a 1D int array is a constant pointer to int, which
points to the first element in that array.
Wrong. I know you might have been told or read this, but it is incorrect. An array is not a pointer!! Arrays and pointers are different types. That being said, for convenience and historical reasons, in most situations (but not all!) an array decays to a pointer to the first element:
int a1[3] = {0, 1, 2};
int* p = a1; // here a1 decays to a pointer to its first element
In the above snippet p points to the element 0 of the array a1
You can view 2D or 3D or nD array the same way:
T a2[3] = {l0, l1, l2};
Let's say T is a type. The above is an "array of 3 Ts".
if T is int then we have int a2[3] = {0, 1, 2} - an array of 3 integers. We call this an 1D int array.
but if T is int[2] then the above becomes int a2[3][2] = {{00, 01}, {10, 11}, {20, 21}} - you can see it as "an array of 3 Ts" or "an array of 3 int[2]" or "an array of 3 arrays of 2 integers".
And we can apply the same decaying rule:
int a2[3][2] = {{00, 01}, {10, 11}, {20, 21}};
int (*p2)[2] = a2; // a2 decays to a pointer to its first element.
// Its first element is an array of 2 int.
// So a2 decays to `int (*)[2]` - a pointer to an array of two elements.
In the above a2 points to the element {00, 01} of the array.
An arrays name is not a pointer. In most cases when the name of an array is used, it gets implicitly *converted* to a pointer to its first element, it is said, that the array decays into a pointer.
The name of an array does not decay to a pointer when it is the argument of the address-of operator (&), the sizeof-operator and when a string literal (which is an array of some character type) is used to initialize an array *).
That said, a two-dimensional arrays
T arr[COLS][ROWS];
first element is an array of type T[ROWS]. So arr decays to a pointer of type T(*)[ROWS] which points to the first element of arr.
*) If you might want to add that arrays also do not decay when they're the operand of the _Alignof-operator or read that somewhere else:
#EricPostpischi: Arrays cannot be operands of _Alignof. Including _Alignof in the exceptions for array conversion was an error in the C 2011 standard. _Alignof operands can only be types, not expressions.
When a 2D array decays to a pointer, you have a pointer to an array. Here's an example of what this would look like:
int arr[5][6];
int (*p)[6] = arr;
An array is not a pointer. An array's name, when used in an expression, "decays" into a pointer to the first element.
Strictly speaking, C only has one-dimensional arrays, and a 2D array is really just an array of arrays.
1D array:
The first element of int arr [x], is an int.
When arr is used in an expression, you get a pointer to that element, int*.
When doing pointer arithmetic on this pointer, each item has the size of the first element = sizeof(arr[0]).
2D array:
The first element of int arr [x][y] is an int [y].
When arr is used in an expression, you get a pointer to that element, int (*)[y].
When doing pointer arithmetic on this pointer, each item has the size of the first element = sizeof(arr[0]).
So it's the same rule. The int(*)[y] array pointer follows the same rules of pointer arithmetic as the ordinary pointer. But you can de-reference it one step further to get individual int in the array of arrays.
"So what will be the value of a 2D int array's name?"
"I actually understand that an array is not a pointer. In my question,
what I actually mean is that when the name of an array is used in an
expression, the compiler will generate the pointer constant."
You have to be careful here. As a follow-on to your comment below your question, there are nuances in how the array/pointer conversion rules apply that effect the type that results from the conversion. That will dictate whether and how you can use the array name in an expression.
"... the compiler will generate the pointer constant."
No. The compiler does not generate a pointer constant, the compiler follows C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3). When the array name is used in an expression, the expression is evaluated with the address that results from the conversion of the array to a pointer (subject to the 4-exceptions stated in paragraph 3).
The rule regarding array/pointer conversion does not depend on number of dimensions, the rule is applied the same regardless. However, the type of the pointer that results from the conversion does depend on the number of array dimensions. That is critical and that will dictate whether your use of the array name is valid.
One way to help cement what is happening in the conversion is take it step-by-step. Start with a 1D array and work your way up.
6.3.2.1 - 1D Array Conversion To Pointer On Access
When you have a simple array, e.g.
int array[10];
On access to array, is converted to a pointer to the first element, e.g., the address of the element, &array[0]. (which is simply a pointer to int, or with formal type int *)
6.3.2.1 - 2D Array Conversion To Pointer On Access
With a 2D array, rule applies just the same, e.g.
int array[10][10];
Here array, a 2D array, is essentially an array of 10 - int[10] arrays (an array of 1D arrays). On access array[10][10] is converted to a pointer to the first array of 10-int in exactly the same manner, &array[0][0] (which results in a pointer to an array of int[10] or with formal type int (*)[10]) It is not a pointer-to-pointer (e.g. int**), it is very specifically a pointer to an array of int[10].
(note the important difference between int *[10] (an array of 10 pointers which on access will become a pointer-to-pointer) and int (*)[10] (a pointer to array of 10 int))
Answer
"So ... the value of a 2D int array's name when used in an expression" -- is the address of the first 1D array of integers that make up the 2D array with formal type int (*)[N] (where N is the number of elements per-row).
Nuance In How The Standard Is Applied
The type is critical for the proper use of the array name. With a 2D array, the resulting address is a pointer to an array. What results if you dereference that pointer? (Answer: an array) What happens when you access that array through the derefernced pointer? (hint: the conversion on access rules apply again). You must know what the pointer type resulting from the conversion will be in order to properly use the array name in an expression.
An Example May Help
Or it may not, but working with the pointer types that result from the array access and pointer conversion may help things sink in. Below, the example declares a simple 4x3 2D array of int. It then declares a pointer (p) of proper type to allow the array name to be used in an expression assigning the array address to the pointer. The pointer initialized with the array name is then used to further initialize an integer pointer (ip) to the first element in the first array.
The example then outputs the address for each element, and then using the pointer p outputs the address of the beginning of each row-array that makes up the 2D array. Finally the code enters a validation loop comparing the addresses of each element by (1) array index, (2) the address held by pointer p using an offset, and (3) address held by ip. The purpose being the use of each of the different pointers resulting from the expression assigning the array name to then reference each element and ensuring the addresses held by each pointer agree.
#include <stdio.h>
int main (void) {
int array[ ][3] = { {1, 2, 3}, /* 2D array values */
{3, 4, 5},
{5, 6, 7},
{7, 8, 9} },
(*p)[3] = array, /* pointer to array */
*ip = *p; /* integer poiner */
size_t size = sizeof array,
nele = size / sizeof **array,
nrow = size / sizeof *array,
ncol = sizeof *array / sizeof **array;
printf ("2D array statistics:\n\n"
" size: %zu (bytes)\n nele: %zu (ints)\n"
" nrow: %zu\n ncol: %zu\n",
size, nele, nrow, ncol);
puts ("\naddress of each array element:\n");
for (size_t i = 0; i < nrow; i++) {
for (size_t j = 0; j < ncol; j++)
printf (" %p", (void*)&array[i][j]);
putchar ('\n');
}
puts ("\naddress of each 1D array:\n");
for (size_t i = 0; i < nrow; i++)
printf (" %p\n", (void*)p[i]);
puts ("\nvalidating each array element address by index & pointer:\n");
for (size_t i = 0; i < nrow; i++) {
for (size_t j = 0; j < ncol; j++) {
if (ip != &array[i][j] || ip != *p + j) {
fprintf (stderr, "address validation failed for "
"array[%zu][%zu]\n(%p != %p || %p != %p)\n",
i, j, (void*)ip, (void*)&array[i][j],
(void*)ip, (void*)(p + j));
return 1;
}
ip++;
}
p++;
}
puts (" done!");
return 0;
}
Example Use/Output
$ ./bin/array_2d_access
2D array statistics:
size: 48 (bytes)
nele: 12 (ints)
nrow: 4
ncol: 3
address of each array element:
0x7ffe7c9a9780 0x7ffe7c9a9784 0x7ffe7c9a9788
0x7ffe7c9a978c 0x7ffe7c9a9790 0x7ffe7c9a9794
0x7ffe7c9a9798 0x7ffe7c9a979c 0x7ffe7c9a97a0
0x7ffe7c9a97a4 0x7ffe7c9a97a8 0x7ffe7c9a97ac
address of each 1D array:
0x7ffe7c9a9780
0x7ffe7c9a978c
0x7ffe7c9a9798
0x7ffe7c9a97a4
validating each array element address by index & pointer:
done!
Let me know if that helped and whether you have any further questions.

Passing an array as double pointer in c warning message

I am trying to run this code but there is a compiler warning:
for the statement invoking the print function
print(arr,3,4);
passing argument 1 of 'print' from incompatible pointer type
[-Wincompatible-pointer-types] main.c /arrays line 23 C/C++ Problem
for the printf statement:
format '%d' expects argument of type 'int', but argument 2 has type
'int *' [-Wformat=] main.c /arrays line 6 C/C++ Problem
The code is to pass a 2D array to a function which receives it as a double pointer and print it inside the function.
void print (int **A, int m, int n){
for(m = 0; m < 3; m++){
for(n = 0; n < 4; n++){
printf("%d ", *((A+(m * 4) + n)));
}
printf("\n");
}
}
int main()
{
int arr[][4]={{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
};
print(arr,3,4);
return 0;
}
1) Passing a 2D array as a double pointer is incorrect in C?
here
The above link points only to C++? or this is possible in C as well?
2) If I am using a single pointer then which of the following assignments are correct/incorrect?
Listing 1:
int *ptr;
ptr = &arr[0][0];
for(int i = 0; i < 3; i++){
for(int j = 0; j < 4; j++){
printf("%d ", *(ptr + (i * 4) +j));
}
printf("\n");
}
Listing 2:
int * ptr;
ptr = arr;
Let’s start with:
int arr[][4]={{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
};
print(arr,3,4);
In print(arr,3,4);, arr is an array. Specifically, it is an array of 3 elements, each of which is an array of 4 elements, each of which is an int. Thus, arr is an array of 3 arrays of 4 int. You have probably heard or read that arrays “decay” to pointers. This is a colloquial term. The actual rule, which you can find in clause 6.3.2.1, paragraph 3, of the C 2011 standard, is:
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.
Here is how this rule applies to arr in print(arr,3,4);:
arr is an identifier, meaning it is the name of some object. As such, it designates its object. That object is an array of 3 arrays of 4 int.
This array is not the operand of sizeof or Alignof or &, and it is not a string literal. So, following the rule, it is converted from an array of 3 arrays of 4 int to a pointer to the first array of 4 int.
What happens next? Nothing. The expression we have is a pointer to an array of 4 int. There is no rule that says a pointer to an array is converted to a pointer to a pointer. We have a pointer to an array, but that array is not used in an expression yet, not in the simple expression arr. So it is not converted.
This means what you are passing to print is a pointer to an array of 4 int. But your declaration for print says it takes a pointer to a pointer to an int. Those are different things, and they are incompatible, so the compiler warns you.
(To see they are incompatible, consider the difference to a pointer to an array of 4 int and a pointer to a pointer to int. The memory at a pointer to an array of 4 int contains 4 int values. The memory at a pointer to a pointer to an int contains a pointer. These are very different things.)
Next, consider:
void print (int **A, int m, int n)
…
printf("%d ", *((A+(m * 4) + n)));
We know from above that you ought to change int **A to int (*A)[4], which is a pointer to an array of 4 int. You can also change it to int A[][4], because there is a rule in C that such a parameter declaration will be automatically adjusted to be int (*A)[4], as a convenience. However, suppose you keep it as int **A. Then does *((A+(m * 4) + n)) mean?
Since A is a pointer to a pointer to an int, then A+(m * 4) means to add m * 4 to the pointer. (That is strange spacing, by the way. m and 4 are more tightly bound by the higher-precedence multiplication than A and (m * 4) are by the addition, so why do they have looser spacing? A + m*4 would portray the meaning better.) Then A+(m * 4) + n means to add n to that. In total, we have moved m*4+n elements beyond where A points. Since A points to a pointer, we have advanced the pointer by m*4+n pointers. Then *((A+(m * 4) + n))) dereferences that. When you dereference a pointer to a pointer to an int, you get a pointer to an int. So the result of this expression is a pointer. But you wanted an int.
The link you reference talks about a “2D array”. The kinds of arrays it talks about are implemented using pointers to pointers. To create such an array, you create an array of pointers, and then you set each of those pointers to point to the elements of a row. Then a pointer to that array of pointers acts like a 2D array, in that A[i][j] refers to element j of row i. If you had an array like that, you could refer to element n of row m using A[m][n]. Equivalently, you could refer to it with *(*(A+m)+n). What this expression means is:
Take the pointer A and add m to it. Since A points to a pointer to an int, adding m advances the value of the pointer to point to m pointers further along. That is where we should find the pointer to the elements of row m.
*(A+m) gets the value of the pointer that A+m points to. This value should be a pointer to the elements of row m, specifically a pointer to the first element (with index 0).
*(A+m)+n advances the value of the pointer to point n int further along. That is where we should find element n of row m.
*(*(A+m)+n) gets the value of the int that *(A+m)+n points to.
Now suppose instead you changed print to be print(int A[][4], int m, int n). Then your printf statement should use A[m][n], just as before. Or it could use *(*(A+m)+n), also just as before. But, in this case, the expression is evaluated:
A is a pointer to an array of 4 int. Adding m to it advances value of the pointer to point m arrays further along.
*(A+m) gets the object that A+m points to. This object is an entire array. So this is an expression that designates an array. Following the C rule about arrays in expressions, this array is converted to a pointer to its first element. Thus, *(A+m) becomes a pointer to the first element of the array numbered m.
*(A+m)+n advances the value of the pointer to point n int further along. That is where we should find element n of row m.
*(*(A+m)+n) gets the value of the int that *(A+m)+n points to.
Thus A[m][n] has the same end result for pointers-to-pointers, for pointers-to-arrays, and for arrays-of-arrays, but the steps it goes through for each are different. C knows the types of each subexpression and processes them differently, to achieve the same result.
Finally, suppose you pass &A[0][0] to print and change its parameter to int *A. Now what is the expression *((A+(m * 4) + n)))? In this case, you are treating the array of 3 arrays of 4 int as one big array of 12 int. Then you calculate where element n of row m is. In this case, A is a pointer to int (not a pointer to a pointer to int). So A+(m * 4) + n is a calculation to where element n of row m ought to be, and *((A+(m * 4) + n))) gets the value of that element.
That is a method you ought to avoid when possible. Generally, you should use C’s built-in methods of addressing array elements and avoid doing your own calculations. Whether it is strictly conforming C code or not may depend on how pedantic you are about interpreting certain passages in the C standard.
You expectation that a 2D array will decay to a pointer to a pointer is ill-founded.
To be able to use arr as an argument to print, you have the following options.
Change print to
void print (int (*A)[4], int m){ // Not need for n. It is 4
Change print to use a VLA. For this to work, m and n have to come before A.
void print(int m, int n, int A[m][n] {
Both these changes will require you to change the call also.
If your intention is an array of pointers you cannot initialize it all at once. Your array compiles to the same thing as {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
The following compiles to an array of pointers to arrays:
int arr1[] = {1, 2, 3, 4};
int arr2[] = {5, 6, 7, 8};
int arr3[] = {9, 10, 11, 12};
int* my2DArr[3] = {arr1, arr2, arr3};
It is valid for a nested loop such as this:
for(int i = 0; i < 3; i++)
{
for(int j = 0; j < 4; j++)
{
printf("%d\n", my2DArr[i][j]);
}
}
For the array in your code, you can iterate over it as if it's just one array.

Pointers in C - 1D and 2D

I know that, for the following:
int a[10];
a is a pointer of the type int * to a[0], while &a is a pointer of type int (*)[10].
Now my question is for the following 2D array:
int b[20][30];
Is b a pointer of the type int **? Or is it a pointer of the type int (*)[30]?
Is &b a pointer of the type int (*)[20][30]?
Arrays are not pointers (this point cannot be stressed enough).
That being said, an array decays to a pointer to its first element. For example:
int a[10];
int b[20][30];
void print_a(int *);
void print_b(int (*)[30]);
print_a(a);
print_b(b);
The first element of a is a[0], and similarly the first element of b is b[0]. You basically take that first dimension away, and change it to a (*); I'll explain more in a moment since it is a bit more complex than that.
The relationship between pointers and arrays is riddled with contextual subtleties that aren't terribly difficult to grasp, but the size information in various scopes makes it interesting and also helps to give you an idea of how the decay works:
#include <stdio.h>
int h(int *pa)
{
printf("h(int *): sizeof pa=%zu\n", sizeof pa);
printf("h(int *): sizeof *pa=%zu\n", sizeof *pa);
return *pa;
}
int g(int (*pa)[5])
{
printf("g(int (*)[5]): sizeof pa=%zu\n", sizeof pa);
printf("g(int (*)[5]): sizeof *pa=%zu\n", sizeof *pa);
return h(*pa);
}
int f(int (*pa)[3][5])
{
printf("f(int (*)[3][5]): sizeof pa=%zu\n", sizeof pa);
printf("f(int (*)[3][5]): sizeof *pa=%zu\n", sizeof *pa);
return g(*pa);
}
int main(void)
{
int arr[2][3][5] = {{{11235}}};
printf("main: sizeof arr=%zu\n", sizeof arr);
printf("main: sizeof *arr=%zu\n", sizeof *arr);
printf("%d\n", f(arr));
}
Every pointer is the same size (this may not always be true on all platforms!), but by dereferencing the pointer, you see the size of a given element of the array, whether you dereference using the unary * operator or the [N] array notation, which is equivalent to *((array)+(N)) by definition.
Anyway, going back to the difference between pointers and arrays, you should understand that int[20][30] is not the same as int **. Why is that? Because of the fact that int[20][30] decays to a pointer of type int(*)[30], no more decay can occur until the pointer is dereferenced. Moreover, int ** is actually int *(*), which means it can point to the first element of an array of pointers. That is, int ** might have once been int *[N].
int foo[x][y][z] <=> int (*foo)[y][z]
int *foo[m][n] <=> int *(*foo)[n]
int (*foo[a])[b] <=> int (**foo)[b]
In the first case, we have a 3-D array, which decays to a pointer to a 2-D array; in other words, an array of arrays and a pointer to an array are closely related and interchangeable in many contexts aside from the size issue. The first dimension x is the one that decays, leaving the y and z dimensions.
In the second case, we have a 2-D array of pointers. This decays to a pointer to an array of pointers. Again, an array of arrays is closely related to a pointer to an array, and dimension m decays, leaving dimension n.
In the third case, we have an array of pointers to arrays. It decays to a pointer to a pointer to an array. Since dimension a is closest to the variable name, that is the one that decays, leaving dimension b. Note that since it is an array of pointers to arrays, the pointers could point to the first element of arrays themselves:
int arr[2][3][5];
int (*foo[2])[5] = { arr[0], arr[1] };
int (**foo_ptr)[5] = foo;
Recap:
Array (size A) of arrays (size B) <=> Pointer to array (size B)
Array (size A) of pointers <=> Pointer to pointer
The array that decays/grows is always the innermost array/pointer, the innermost being the one closest to the variable name in the variable declaration, with arrays having higher associativity than pointers, though of course parentheses make all the difference.
This rabbit hole obviously can be confusing, but I hope I helped a bit at least!
First of all make it clear that arrays are not pointers. Relationship between pointers and arrays is subtle. I would suggest you to read second chapter of tutorial on pointers by Ted Jensen first.
I would like to tell you something in a nutshell what has been describe in this chap. Consider following example.
int a[10];
int *p;
Now you can write
p=a;
that is equivalent to
p=&a[0];
This thing make many texts to say array is name of pointer. But it is better if you say "the name of the array is the address of first element in the array" .
Because though you can write
p=a;
but you can not write
a=p;
Now come to your question:
From above discussion it should be clear that b is not pointer of type int** .For example:
int b[10][10];
int **x;
int *p;
b=&p; // this is error
x=&p; // this fine
For your other questions you may use online CDECL.
if you write
int b[10][10]; --> declare b as array 10 of array 10 of int
int (*p)[10]; --> declare p as pointer to array 10 of int
int (*p)[10][20]; --> declare p as pointer to array 10 of array 20 of int
No, a is not of type int*, it is of type int [10] (i. e. of an array type). This is why sizeof(a) will give you the size of the array (40 bytes, assuming 32 bit integers).
Likewise, b is of type int [20][30], which is nothing other than an array of arrays. I. e. sizeof(b[0]) is the size of one line array, which is 120 in this case, the size of the entire array (sizeof(b)) is 20 times the size of the line arrays, which is 2400 bytes in this case.
The trick is, that an array decays into a pointer to its first element in almost all contexts. So when you do pointer arithmetic (as in b[3]) on the 2D array, b decays into a pointer of type int (*)[30], so the pointer arithmetic skips rows, adding three times the size of a row (360 bytes in this case) - the rows are the elements of the 2D array. The resulting type of b[3] is int [30], i. e. the dereferenced pointer.
Once you have dereferenced to a row array with b[3], you can again invoke pointer arithmetic to select the correct element in this row (b[3][5]). Again, the array-pointer-decay is invoked, the mechanic is the same.
Note that there is no pointer array involved as is the case when you emulate a 2D array with an int**. The double dereference b[3][5] translates into something like ((int*)b)[3*30 + 5] by virtue of array-pointer-decay, only the element itself is accessed from memory.
int* temp;
int arraySize = 20;
temp = (int *)malloc(arraySize * sizeof(int));
This will create a section in memory 20 "ints" long, similarly as you mentioned.
int** temp;
int arraySize = 20;
int rowSize = 10;
temp = (int **)malloc(arraySize * sizeof(int *));
for(i=0; i<arraySize; i++){
temp[i] = (int *)malloc(rowSize * sizeof(int));
}
That is what the 2D array would actually look like.
temp[0] would give you the address of the first "array". In which you could do something like above int *array = temp[0] then access it like a normal array but using *array[0] to get the value.
2D arrays really don't mesh well with pointers and saying *temp[0] to get the values of the first array. You can try to mess with it, you'll figure it out, but don't have a machine that can compile C with me right now.
Reference that may help: http://www.cs.swarthmore.edu/~newhall/unixhelp/C_arrays.html

how does p become a 2-D array after the call to malloc?

The following snippet declares a 2-D array of 4 X 10 using malloc function
/* Declare a pointer to an array that has 10
ints in each row. */
int (*p)[10];
register int i, j;
/* allocate memory to hold a 4 x 10 array */
p = malloc(40*sizeof(int));
But I do not understand how does p become a 2-D array. Initially p is declared to be an array of pointers that point to int. What happens after the call to malloc ? I am unable understand this.
In C, pointers and arrays are not the same, despite looking very similar. Here p is of type "pointer to array of 10 ints". You're using it as a "pointer to array of 4 arrays of 10 ints", which is a single block of memory (the only pointer is the outermost pointer). It's basically a dynamically allocated int[4][10].
The trick to reading these definitions is to realize that they're written the same way you use the item. If you have:
*x[10];
The array subscript is applied first, then the pointer dereference. So it's an array of pointers if you define int *x[10]. If you use parenthesis to override normal precedence, you can get the pointer dereference to happen first, so you have a pointer to an array.
Confusing? It gets worse. In function arguments, the outermost array of a function parameter is converted into a pointer.
int *p[10]; // array of 10 pointer to int
int (*p)[10]; // pointer to array of 10 ints
void foo(int *p[10] /* pointer to pointer to int */);
void foo(int (*p)[10] /* pointer to array of 10 ints */);
Further, arrays are converted to pointers when you use them.
int x[10]; // array of 10 int
sizeof(x); // 10 * sizeof(int)
int *y = x; // implicitly converts to a pointer to &x[0]!
sizeof(y); // sizeof(int *)
This means that you can allocate memory for an array of arrays, then let that implicitly convert to a pointer to an array, which you in turn use as if it were an array of arrays!
Anyway, this is all very confusing so please do not ever use this in production code - at least, not without a clarifying typedef:
typedef int vector[3];
vector *array_of_vectors; // actually a pointer to a vector,
// but you can use it as an aray to a vector if enough
// memory is allocated
The memory, worth 40 ints, is reserved to the pointer p. p points at his memory block. It so happens that p chooses to organize this memory as 10 equal parts, each of which happen to hold 4 ints' worth.
That's if this code is actually correct. My C is very rusty at this point.
First, some background information:
Except when it is the operand of the sizeof, _Alignof, 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" is converted ("decays") to an expression of type "pointer to T", and its value is the address of the first element in the array. For example, given the array
int a[10];
anytime the expression a appears in the code, its type will be converted from "10-element array of int" to "pointer to int", or int *, except for cases like sizeof a, _Alignof a, and &a. If we have a 2D array of T, such as
int a[10][10];
the expression a will be converted from type "10-element array of 10-element array of int" to "pointer to 10-element array of int", or int (*)[10] (look familiar? that's the type of your pointer p).
If we want to dynamically allocate an N-element array of type T, we write something like
T *p = malloc(N * sizeof *p);
sizeof *p is equivalent to sizeof (T). In this particular case, type T is "10-element array of int", or int [10]. We want to allocate 4 such arrays, so we can write
int (*p)[10];
p = malloc(4 * sizeof *p);
This allocates space for 4 10-element arrays of int, and assigns the result to p. (sizeof *p == sizeof (int [10])).
So how does this become a 2D array?
Remember that the expression a[i] is equivalent to *(a + i); we find the address of the i'th element of type T following a and dereference the result. In this case p[i] gives us the address of the ith 10-element array of int following p. Since we dereference the pointer as part of the subscript operation, the type of the expression p[i] is "10-element array of int". Thus we can subscript this expression again and get p[i][j].

Resources