Passing an array as double pointer in c warning message - c

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.

Related

Explanation how pointers and multidimensional arrays work in C

I am trying to understand the following code.
#include <stdio.h>
#include <stdlib.h>
void print2(int (* a)[2]) {
int i, j;
for (i = 0; i < 3; i++ ) {
for (j = 0; j < 2; j++ ) {
printf("%d", a[i][j]);
}
printf("\n");
}
}
void print3(int (* a)[3]) {
int i, j;
for (i = 0; i < 2; i++ ) {
for (j = 0; j < 3; j++ ) {
printf("%d", a[i][j]);
}
printf("\n");
}
}
int main() {
int a[] = { 1, 2, 3, 4, 5, 6 };
print2((int (*)[2]) a);
print3((int (*)[3]) a);
return 0;
}
Running the code returns following output in console:
12
34
56
123
456
My problem is I don't understand where these numbers come from. I have trouble understanding what is actually going on in this code. More specifically, I'm uncertain of what this means:
int( (* a)[2])
I hope someone can explain this code to me, because I really want to understand how pointers and multidimensional arrays work in C.
TL;DR
This code contains incorrect and meaningless hacks. There is not much of value to learn from this code.
Detailed explanation follows.
First of all, this is a plain 1D array that gets printed in different ways.
These lines are strictly speaking bugs:
print2((int (*)[2]) a);
print3((int (*)[3]) a);
In both cases there is an invalid pointer conversion, because a is of type int[6] and a pointer to the array a would have to be int (*)[6]. But the print statements are wrong in another way too, a when used in an expression like this "decays" into a pointer to the first element. So the code is casting from int* to int(*)[2] etc, which is invalid.
These bugs can in theory cause things like misaligned access, trap representations or code getting optimized away. In practice it will very likely "work" on all mainstream computers, even though the code is relying on undefined behavior.
If we ignore that part and assume void print2(int (*a)[2]) gets a valid parameter, then a is a pointer to an array of type int[2].
a[i] is pointer arithmetic on such a type, meaning that each i would correspond to an int[2] and if we had written a++, the pointer would jump forward sizeof(int[2]) in memory (likely 8 bytes).
Therefore the function abuses this pointer arithmetic on a[i] to get array number i, then do [j] on that array to get the item in that array.
If you actually had a 2D array to begin with, then it could make sense to declare the functions as:
void print (size_t x, size_t y, int (*a)[x][y])
Though this would be annoying since we would have to access the array as (*a)[i][j]. Instead we can use a similar trick as in your code:
void print (size_t x, size_t y, int (*a)[x][y])
{
int(*arr)[y] = a[0];
...
arr[i][j] = whatever; // now this syntax is possible
This trick too uses pointer arithmetic on the array pointer arr, then de-references the array pointed at.
Related reading that explains these concepts with examples: Correctly allocating multi-dimensional arrays
void print2(int (*a)[2]) { /*...*/ }
inside the function print2 a is a pointer to arrays of 2 ints
void print3(int (*a)[3]) { /*...*/ }
inside the function print3 a is a pointer to arrays of 3 ints
int a[] = {1, 2, 3, 4, 5, 6};
inside the function main a is an array of 6 ints.
In most contexts (including function call context) a is converted to a pointer to the first element: a value of type "pointer to int".
The types "pointer to int", "pointer to array of 2/3 ints" are not compatible, so calling any of the functions with print2(a) (or print3(a)) forces a diagnostic from the compiler.
But you use a cast to tell the compiler: "do not issue any diagnostic. I know what I'm doing"
print3(a); // type of a (after conversion) and type of argument of print3 are not compatible
// print3((cast)a); // I know what I'm doing
print3((int (*)[3])a); // change type of a to match argument even if it does not make sense
It would be much easier to understand if you break it down and understand things. What if you were to pass the whole array to say a function print4, which iterates over the array and prints the elements? How would you pass the array to such a function.
You can write it something like
print4( (int *) a);
which can be simplified and just written as print4(a);
Now in your case by doing print2((int (*)[2]) a);, you are actually designing a pointer to an array of 2 int elements. So now the a is pointer in array of two elements i.e. every increment to the pointer will increase the offset by 2 ints in the array a
Imagine with the above modeling done, your original array becomes a two dimensional array of 3 rows with 2 elements each. That's how your print2() element iterates over the array a and prints the ints. Imagine a function print2a that works by taking a local pointer to a and increments at each iteration to the point to the next two elements
void print2a(int (* a)[2]) {
int (* tmp)[2] = a;
for( int i = 0; i < 3; i++ ) {
printf("%d%d\n", tmp[0][0], tmp[0][1] );
tmp++;
}
}
The same applies to print3() in which you pass an pointer to array of 3 ints, which is now modeled as a 2-D array of 2 rows with 3 elements in it.
The code seeks to reinterpret the array int a[6] as if it were int a[3][2] or int a[2][3], that is, as if the array of six int in memory were three arrays of two int (in print2) or two arrays of three int (in print3).
While the C standard does not fully define the pointer conversions, this can be expected to work in common C implementations (largely because this sort of pointer conversion is used in existing software, which provides motivation for compilers to support it).
In (int (*)[2]) a, a serves as a pointer to its first element.1 The cast converts this pointer to int to a pointer to an array of two int. This conversion is partially defined C 2018 6.3.2.3 7:
The behavior is undefined if the alignment of a is not suitable for the type int (*)[2]. However, compilers that have stricter alignment for arrays than for their element types are rare, and no practical compiler has a stricter alignment for an array of six int than it does for an array of two or three int, so this will not occur in practice.
When the resulting pointer is converted back to int *, it will compare equal to the original pointer.
The latter property tells us that the resulting pointer contains all the information of the original pointer, since it must contain the information needed to reconstruct the original pointer. It does not tell us that the resulting pointer is actually pointing to the memory where a is.
As noted above, common C implementations allow this. I know that Apple’s versions of GCC and Clang support this reshaping of arrays, although I do not know whether this guarantee was added by Apple or is in the upstream versions.
Given that (int (*)[2]) is passed to print2 as its a, then a[i][j] refers to element j of array i. That is, a points to an array of two int, so a[0] is that array, a[1] is the array of two int that follows it in memory, and a[2] is the array of two int after that. Then a[i][j] is element j of the selected array. In effect, a[i][j] in print2 is a[i*2+j] in main.
Note that no aliasing rules are violated as no arrays are accessed by a[i][j]: a is a pointer, a[i] is an array but is not accessed (it is automatically converted to a pointer, per footnote 1 below), and a[i][j] has type int and accesses an object with effective type int, so C’s aliasing rules in C 2018 6.5 7 are satisfied.
Footnotes
1 This is because when an array is used in an expression, it is automatically converted to a pointer to its first element, except when it is the operand of sizeof, is the operand of unary &, or is a string literal used to initialize an array.

How can an integer be handled like an array as an in-out parameter?

I'm a Swift programmer using C code in a project and I cannot, for the life of me, figure out how the kRing function (below) works from the Uber H3 library (https://github.com/uber/h3/blob/master/examples/neighbors.c). H3Index is a 64-bit integer, that's all you need to know.
H3Index indexed = 0x8a2a1072b59ffffL;
int k = 2;
int maxNeighboring = maxKringSize(k);
H3Index* neighboring = calloc(maxNeighboring, sizeof(H3Index));
kRing(indexed, k, neighboring);
for (int i = 0; i < maxNeighboring; i++) {
if (neighboring[i] != 0) {
// whoa, neighboring is now magically an array, it was just an integer
}
}
From Uber's documentation:
void kRing(H3Index origin, int k, H3Index* out);
Output is placed in the provided array in no particular order. Elements of the output array may be left zero, as can happen when crossing a pentagon.
But the output is an in-out parameter (that's what we call them in Swift, don't know what they're called in C) that's an integer type, not an array type.
Documentation on the function: https://h3geo.org/#/documentation/api-reference/traversal
In C an int* (int pointer) is just an address that points to an int. It can be used as an array:
int array[10];
int oneInt;
void someFunc(int* ip)
{
...
}
someFunc(array);
someFunc(&oneInt);
/* are both valid calls */
In the above example the person calling someFunc needs to know whether the pointer is being treated as a single int or an array (and if an array: how many elements ?).
This seems to be handled in the API you've given:
H3Index* neighboring = calloc(maxNeighboring, sizeof(H3Index));
/* gives you a pointer to an array of maxNeighboring indexes */
kRing(indexed, k, neighboring);
/* calls kRing and passes your array. kRing can use parameter "k" to
figure out how many elements it can play with
*/
for (int i = 0; i < maxNeighboring; i++) { .. }
/* zeros each element in the array.
Kind of weird. You just called kRing to populate the array and now you're
zeroing it all out ? Seems the call to kRing was redundant in the first
place ?
*/
In the posted code, neighboring is a pointer to H3Index. With the expression neighboring[i], neighboring is not an integer type, but is still a pointer type; yet neighboring[i] does have type H3Index. The confusion seems to be around the use of array subscripting here.
Pointers and arrays are distinct types in C, but array subscripting can be used with pointers. The C Standard offers §6.5.2.1p2:
A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).
That is, array subscripting is equivalent to pointer arithmetic. In the posted code, the array subscript expression neighboring[i] is equivalent to the pointer arithmetic expression *(neighboring + i).
Note that "...if E1 is an array object (equivalently, a pointer to the initial element of an array object)..." does not mean that a pointer to the initial element of an array is equivalent to that array. Arrays and pointers are distinct types, which can be seen with, e.g.:
#include <stdio.h>
int main(void) {
printf("Array of 20 ints: %zu bytes\n", sizeof (int [20]));
printf("Pointer to int: %zu bytes\n", sizeof (int *));
return 0;
}
On my machine, this outputs:
Array of 20 ints: 80 bytes
Pointer to int: 8 bytes
Here, with 4 byte ints, an array of 20 ints takes up 80 bytes, but a pointer to an int only takes up 8 bytes. These are two distinct types with two different sizes.
With an actual array (say, defined as int arr[20] = {0};), arr[i] must behave as the expression *(arr + i), where the array identifier arr has decayed to a pointer to the first element of the array arr[]. Array expressions (i.e., expressions with an array type) decay to pointers to the first element of the array in most circumstances, and they do so here as well. arr would decay to a pointer in either expression: arr + 1, or in arr[i] (in this case arr is a postfix expression followed by a postfix operator).
There are a few circumstances under which an array expression will not decay to a pointer: if the array expression is an operand to the sizeof operator, the unary & address operator, or if the array expression is a string literal used to initialize another array §6.3.2.1p3. Postfix array subscripting is not one of these cases.
This all means that array subscripting applied to an array is equivalent to array subscripting applied to a pointer to the first element of that array:
#include <stdio.h>
int main(void) {
int arr[20] = { 1, 2, 3, 4, 5 };
// Note: this will not work for `int *ptr = &arr`;
// that would have type int (*)[20],
// i.e, pointer to array of 20 `ints`.
int *ptr = &arr[0];
printf("arr[2] = %d\n", arr[2]);
printf("ptr[2] = %d\n", ptr[2]);
return 0;
}
Here, the expressions arr[2] and ptr[2] are equivalent (and both expressions have type int in this case). But by the same token, in the OP posted code, neighboring[i], where neighboring is just a pointer to H3Index, behaves as if neighboring were an array of H3Index values, yet neighboring is not such an array.

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.

Why does the dereference operator fail to return value pointed by a "pointer to an array" variable?

Consider the program,
#include <stdio.h>
int main(){
int a[][4] = {2,3,4,5,
43,32,76,3
};
int *p;
int (*q)[4];
p = (int *)a;
q = a;
printf("%u\t%u", (unsigned int)p,(unsigned int)q);
printf("\n%d\t%d",*(++p),*(++q));
return 0;
}
In the above program, I have defined two pointers,
1) Integer pointer
2) Pointer to an Array
When compiled using the GCC compiler, I met with two doubts,
Question 1:
Why is that, the statement,
printf("\n%d\t%d",*(++p),*(++q));
returns an address when I try to dereference (++q)?
Question 2:
Is there any way I can use a "Pointer to an array" variable('q' - in this program) to access the consecutive element in a row?
Compiler: GCC; OS: Ubuntu
*(++q) does not "return an address". It returns a result of type int[4], which is an lvalue of array type. That lvalue refers to a[1] subarray of array a.
This means that the title of your question is no accurate at all: the dereference operator in this case does return exactly what it is supposed to return. The pointer is declared as a pointer to an array, and the dereference operator evaluates to an lvalue of array type.
However, immediately after that you use that array lvalue in a context where it decays to pointer type, just like any other array would (see What is array decaying?, Why do arrays in C decay to pointers?). Passing an array as an argument to printf happens to be such a context. So, that original array lvalue decays to a pointer. That pointer value points to the beginning of a[1], which is the same as &a[1][0]. That pointer value is passed to printf and you attempt to print it with %d.
(Note that using %d in printf to print pointer values triggers undefined behavior.)
Well, you can access them in any way you wish
q = a;
q[1][1] = 42; // accesses `a[1][1]`
++q;
(*q)[2] = 5; // accesses `a[1][2]`
This declaration
int (*q)[4];
declares a pointer to objects of type int[4]. To simplify the understanding you could introduce a typedef the following way
typedef int T[4];
T *q;
q = a;
So dereferencing the pointer you will get the pointed object of the type T that represents an array of type int[4]. As result using this object in the printf function
printf("\n%d\t%d",*(++p),*(++q));
^^^^^
you will get the second "row" (due to incrementing the pointer ++q) of the array a that is in turn a one-dimensional array of type int[4] that in turn is implicitly converted to pointer to its first element.
Thus the expression *(++q) has the type int * and points to the first element of the second "row" of the array a.
If you want to use this pointer to traverse the elements of the array a you can do it the following way
#include <stdio.h>
int main(void)
{
int a[][4] = { { 2, 3, 4, 5 }, { 43, 32, 76, 3 } };
int ( *q )[4] = a;
for ( size_t i = 0; i < sizeof( a ) / sizeof( *a ); i++ )
{
for ( size_t j = 0; j < sizeof( *q ) / sizeof( **q ); j++ )
{
printf( "%2d ", q[i][j] );
}
printf( "\n" );
}
return 0;
}
The program output is
2 3 4 5
43 32 76 3
That is q[0] is the first "row" of the array a. You can write also just *q. q[1] is the second "row" of the array a. You may can write also like *( q + 1 ). To get access to the elements of each row you can apply the subscript operator like (the first row)
q[0][i] where i is some index value. Or you can write the same like ( *q )[i]
and like (the second row) q[1][i] or ( *( q + 1 ) )[i]
And the special expression like **q yields the first element of the first row of the array a.
TL;DR answer, dereferencing a pointer-to-an array produces an array, which, once passed as a function argument, decays to pointer to the first element of the array. So, the final value is of pointer type (the former and the later are of pointer to different type, but they are pointers, nonetheless).
That said, first of all
printf("%u\t%u", (unsigned int)p,(unsigned int)q);
is implementation dependent behaviour as conversion of a pointer to an integer is implementation defined. If at all, you can cast the pointer to uintptr_t type and print using PRIuPTR format specifier to print the value.
Secondly, pointer arithmetic honors the data type. q is a pointer to an array of 4 ints. Once incremented, it points past the first array. But then, there's a mismatch in the supplied format specifier.
printf("\n%d\t%d",*(++p),*(++q));
^^ ^^^^^^
To elaborate, q is of type pointer-to-array. Dereferencing that produces an array, now once the array is passed as an argument to a function, it decays to the pointer to the first element of the array. So, essentially, you're passing a pointer as the argument to %d format specifier, which is the mismatch, leading to undefined behavior.
Finally, regarding the access to individual elements, you can either use
(*(q+m))[n] --> First dereference q to obtain the array, then use indexing
q[m][n] --> q[m] produces the array, then n is used as indexing to get individual element.

Access by pointer to multi-dimensional static arrays in 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!

Resources