Please consider the following 2-D Array:
int array[2][2] = {
{1,2},
{3,4}
};
As per my understanding:
- 'array' represents the base address of the 2-D array (which is the same as address of the first element of the array, i.e array[0][0]).
The actual arrangement of a 2-D Array in memory is like a large 1-D Array only.
Now, I know that base address = array. Hence, I should be able to reach the Memory Block containing the element: array[0][0].
If I forget about the 2-D array thing & try to treat this array as a simple 1-D array:
array[0] = *(array+0) gives the base address of the first array & NOT the element array[0][0]. Why?
A 2-D array does not store any memory address (like an Array of Pointers).
If I know the base address, I must be able to access this memory as a linear 1- Dimensional Array.
Please help me clarify this doubt.
Thanks.
array[0] is a one-dimensional array. Its address is the same as the address of array and the same as the address of array[0][0]:
assert((void*)&array == (void*)&(array[0]));
assert((void*)&array == (void*)&(array[0][0]));
Since array[0] is an array, you can't assign it to a variable, nor pass it to a function (if you try that, you'll be passing a pointer to the first element instead). You can observe that it's an array by looking at (array[0])[0] and (array[0])[1] (the parentheses are redundant).
printf("%d %d\n", (array[0])[0], (array[0])[1]);
You can observe that its size is the size of 2 int objects.
printf("%z %z %z\n", sizeof(array), sizeof(array[0]), sizeof(array[0][0]));
Here's a diagram that represents the memory layout:
+-------------+-------------+-------------+-------------+
| 1 | 2 | 3 | 4 |
+-------------+-------------+-------------+-------------+
`array[0][0]' `array[0][1]' `array[1][0]' `array[1][1]'
`---------array[0]---------' `---------array[1]---------'
`-------------------------array-------------------------'
"Thou shalt not fear poynter arythmethyc"...
int array[2][2] = { { 1, 2}, { 3, 4 } };
int *ptr = (int *)&array[0][0];
int i;
for (i = 0; i < 4; i++) {
printf("%d\n", ptr[i]);
}
Why does this work? The C standard specifies that multidimensional arrays are contigous in memory. That means, how your 2D array is arranged is, with regards to the order of its elements, is something like
array[0][0]
array[0][1]
array[1][0]
array[1][1]
Of course, if you take the address of the array as a pointer-to-int (int *, let's name it ptr), then the addresses of the items are as follows:
ptr + 0 = &array[0][0]
ptr + 1 = &array[0][1]
ptr + 2 = &array[1][0]
ptr + 3 = &array[1][1]
And that's why it finally works.
The actual arrangement of a 2-D Array in memory is like a large 1-D Array only.
yes, the storage area is continuous just like 1D arrary. however the index method is a little different.
2-D[0][0] = 1-D[0]
2-D[0][1] = 1-D[1]
...
2-D[i][j] = 1-D[ i * rowsize + j]
...
If I forget about the 2-D array thing & try to treat this array as a simple 1-D array: array[0] = *(array+0) gives the base address of the first array & NOT the element array[0][0]. Why?
the *(array+0) means a pointer to a array. the first element index in such format should be *((*array+0)+0).
so finally it should be *(*array)
A 2-D array does not store any memory address (like an Array of Pointers).
of course, you can . for example ,
int * array[3][3] ={ null, };
If I know the base address, I must be able to access this memory as a linear 1- Dimensional Array.
use this formal 2-D[i][j] = 1-D[ i * rowsize + j]...
Arrays are not pointers.
In most circumstances1, 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.
The type of the expression array is "2-element array of 2-element array of int". Per the rule above, this will decay to "pointer to 2-element array of int (int (*)[2]) in most circumstances. This means that the type of the expression *array (and by extension, *(array + 0) and array[0]) is "2-element array of int", which in turn will decay to type int *.
Thus, *(array + i) gives you the i'th 2-element array of int following array (i.e., the first 2-element array of int is at array[0] (*(array + 0)), and the second 2-element array of int is at array[1] (*(array + 1)).
If you want to treat array as a 1-dimensional array of int, you'll have to do some casting gymnastics along the lines of
int *p = (int *) array;
int x = p[0];
or
int x = *((int *) array + 0);
1. The exceptions are when the array expression is an operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration.
I like H2CO3's answer. But you can also treat the pointer to the array as an incrementable variable like so:
int array[2][2] = { { 1, 2}, { 3, 4 } };
int *ptr = (int *)array;
int i;
for (i = 0; i < 4; i++)
{
printf("%d\n", *ptr);
ptr++;
}
the ++ operator works on pointers just fine. It will increment the pointer by one address of it's type, or size of int in this case.
Care must always be used with arrays in c, the following will compile just fine:
int array[2][2] = { { 1, 2}, { 3, 4 } };
int *ptr = (int *)array;
int i;
for (i = 0; i < 100; i++) //Note the 100
{
printf("%d\n", *ptr);
ptr++;
}
This will overflow the array. If you are writing to this you can corrupt other values in the program, including the i in the for loop and the address in the pointer itself.
Related
Here's the code that I am trying to work with.
int main(void) {
int array[2][5];
for (int ** p = array; p < array + 2; p++) {
*((*p) + 2) = 20;
}
for (int row = 0; row < 2; row++) {
for (int col = 0; col < 5; col++) {
printf("%d ", array[row][col]);
}
printf("\n");
}
return EXIT_SUCCESS;
}
And this is the output given out by the compiler.
main.c:11:21: warning: initialization of 'int **' from incompatible pointer type 'int (*)[5]' [-Wincompatible-pointer-types]
11 | for (int ** p = array; p < array + 2; p++) {
| ^~~~~
main.c:11:30: warning: comparison of distinct pointer types lacks a cast
11 | for (int ** p = array; p < array + 2; p++) {
| ^
main.exe
make: *** [Makefile:7: main] Error -1073741819
I clearly don't understand type casting when it comes to multidimensional arrays. It would really help if you could explain why this doesn't work.
Omitting the first for loop and doing things manually like this do seem to give out the intended result.
*((*array) + 2) = 20;
*(*(array + 1) + 2) = 20;
I can't get the hang of "int (*)[5]".
The type of array is int [2][5], an array of two arrays of 5 int.
When an array is used in an expression other than as the operand of sizeof, the operand of unary &, or as a string literal used to initialize an array, it is automatically converted to a pointer to its first element.
Since the type of array is int [2][5], the type of its first element is int [5], an array of 5 int. So the type of a pointer to its first element is int (*)[5], a pointer to an array of 5 int.
There is no rule that a pointer to an array is converted to a pointer to a pointer. So no further automatic conversion happens. The result of using array in an expression where it is automatically converted is an int (*)[5].
Therefore, p should be declared to be int (*)[5]:
for (int (*p)[5] = array; p < array + 2; p++)
A multi-dimensional array in C can be seen as an array of arrays. Each of the two top-level elements in array[2][5] is a one-dimensional array of 5 ints. Like any old array, array "decays" in most expressions to a pointer to its first element, that is, a pointer to an int[5]. A declaration for such a pointer would be int (*firstRow)[5] = array; (initializing it with the address to the first top-level element of array). The parentheses are necessary because the indexing operator [] has higher priority than the dereferencing operator *: int *arrayOf5Pointers[5]; would index first, and the result would be a pointer, so that it declares a real array (we can index it) whose elements in turn are pointers (we can dereference the result of the indexing). With (*firstRow)[5]; we indicate that we dereference first (the variable is a pointer) and only then index the result of the dereferencing (which must be an array).
The notation for the type of (*firstRow)[5]; (what you would use in a type cast) is, as always, the notation for a declaration without the identifier, hence int (*)[5]. That looks funny and perhaps ambiguous; but Kernighan/Richie said in their C book that the C grammar guarantees that there is only one valid position for an identifier in such a type notation. I believe them.
Your assignment int ** p = array is wrong: You cannot assign a (pointer to an array of 5 ints) to (a pointer to pointer to int). That is true even though you can legitimately access the first element of the two-dimensional array with e.g. int i = **array;: array decays to a pointer to its first element, that is, to a pointer to an array of 5 ints; the first dereferencing results in the actual array of 5 ints; which in turn decays to a pointer to its first int element; which is dereferenced with the second *.
The general takeaway here is: Pointers to arrays are rare and typically unnecessary. Simply index your 2-dimensional array in the normal fashion:
for(int row = 0; row < 2; row++) { array[row][2] = 20; }
or, better,
for(int row = 0; row < sizeof array/sizeof *array; row++) { array[row][2] = 20; }
The latter determines the number of top-level elements in the array by dividing its overall size in bytes by the size of its first element. The loop stays correct even if you change the size of array.
As I was going though some code examples, I encountered this way of declaration:
int (*arr1)[10]
I am aware of another way of array declaration in C:
int arr2[10]
Is int (*arr1)[10] doing the same as int arr2[10]?
The differences are as follows:
int (*arr1)[10]; // arr1 is a pointer to a 10-element array of int
int *arr2[10]; // arr2 is a 10-element array of pointer to int
This is important - despite the [10] at the end, arr1 is not an array; it only stores a single pointer value, which is the address of some 10-element array of int.
Pointers to arrays crop up in two main circumstances - when an N-dimensional array expression "decays" to a pointer expression, and when we're allocating memory for an N-dimensional array.
First, the decay rule - except when it is the operand of the sizeof or the 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, or "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. You've probably seen this before with code like:
int arr[10];
int *p = arr; // arr "decays" to a pointer to its first element
The expression arr "decays" from type "10-element array of int" to type "pointer to int".
The exact same thing happens with 2D arrays:
int arr[5][10];
int (*p)[10] = arr;
The expression arr "decays" from type "5-element array of 10-element array of int" to type "pointer to 10-element array of int".
You can also obtain a pointer to a 1D array directly with the unary & operator:
int arr[10];
int (*p)[10] = &arr; // note presence of & operator here
but that's not very common.
Another use for pointers to arrays is when we want to dynamically allocate a contiguous 2D (or higher-dimensioned) array:
int (*p)[10] = malloc( sizeof *p * 5 ); // allocates space for a 5x10 array of int
The type of the expression *p is "10-element array of int", or int [10], so sizeof *p is equivalent to sizeof (int [10]), so the malloc call sets aside enough space for 5 10-element arrays of int. You would index into this array like any other 2D array: p[i][j] = some_value();. The rows of this array are contiguous in memory and all the same length, and it only requires a single call to free to deallocate.
You've probably seen code that dynamically allocates 2D arrays in multiple steps - first you allocate an array of pointers, then for each of those pointers you allocate an array of the target type, like so:
int **p = malloc( sizeof *p * R ); // allocates an R-element array of pointer to int
if ( p )
{
for ( size_t i = 0; i < R; i++ )
{
p[i] = malloc( sizeof *p[i] * C ); // allocates a C-element array of int
}
}
The difference here is that in the second case, the rows are not adjacent in memory - it's not a single, continuous block. Also, different rows may be different lengths (what's sometimes called a "jagged" array). You also have to free each row separately before freeing p.
The basic rules of pointer declarations are:
T *p; // p is a pointer to T
T *a[N]; // a is an array of pointer to T
T *f(); // f is a function returning pointer to T
T (*a)[N]; // a is a pointer to an N-element array of T
T (*f)(); // f is a pointer to a function returning T
T const *p; // p is a non-const pointer to const T - you can modify p to point
// to a different object, but you cannot modify the thing p points to
const T *p; // same as above
T * const p; // p is a const pointer to non-const T - you can modify what p
// p points to, but you can't modify p to point to a different object
// (which means p needs to be initialized to a valid pointer value
// when it's declared).
T const * const p; // p is a const pointer to const T - you can't update either
// p or *p
const T * const p; // same as above.
If in this specification
int (*arr1)[10]
you will remove the term in the parentheses you will get int[10]. The term in the parentheses denotes a pointer. So you have a pointer to an array of 10 elements of the type int. For example
int a[10];
int ( *arr )[10] = &a;
Dereferencing the pointer you will get the array (the object of the array type pointed to by the pointer) itself.
Here is a demonstrative program.
#include <stdio.h>
int main(void)
{
enum { N = 10 };
int a[N] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int ( *arr )[N] = &a;
for ( size_t i = 0; i < N; i++ )
{
printf( "%d ", ( *arr )[i] );
}
putchar( '\n' );
return 0;
}
The program output is
0 1 2 3 4 5 6 7 8 9
Instead of these declarations you could use declarations based on a typedef like
enum { N = 10 };
typedef int Array[N];
Array a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Array *arr = &a;
I'm trying to understand what is the difference between an array like this:
int arr[2][2] = {{0, 1}, {2, 3}};
int* pArr = (int*)arr;
for(int i = 0; i < 4; i++)
{
printf("%d ", pArr[i]);
}
and this:
int* foo[2] = {arr1, arr2}; // Let's say the length of arr1 is 3 and arr2 is 1
int* pFoo = (int*)foo;
for(int i = 0; i < 4; i++)
{
printf("%d ", pFoo[i]);
}
They look pretty much the same to me but the output is completely different.
I'm getting strange results, if i do what i gave here as an example than it give me big integers, but if i add more arrays and items it gives me smaller integers too.
output example:
Output
*in the outout image: The upper integers are the first 2d array and the bottom integers are the second array pointer.
Can someone explain to me please why this behavior is happening?
A multi-dimensional array is a single block of memory. An array of pointers to data that is not necessarily contiguous (a single block).
The latter is useful for managing sparse arrays or where each pointed to subarray isn't necessarily the same size.
Can someone explain to me please why this behavior is happening?
With ...
int* foo[2] = {arr1, arr2}; // Let's say the length of arr1 is 3 and arr2 is 1
... foo is declared as an array whose elements are of type int *. You do not present the definitions of arr1 and arr2, but let's say they are arrays of int -- then in the initializer expressions they "decay" to pointers to their first elements, which have the correct type for elements of foo, so that's all fine.
But pointers are not integers. You declare pFoo as an int *, but you initialize it with an int ** converted to int *:
int* pFoo = (int*)foo;
Converting the pointer's type does nothing to the data to which it (actually) points, and since pFoo ends up pointing to data that aren't actually ints, accessing those data through pFoo produces undefined behavior.
Perhaps you were looking for this:
int **pFoo2 = foo; // note: no cast needed
Now pFoo2's type, int **, is the same as the type to which foo naturally decays, and you can access the elements correctly:
printf("%d", pFoo2[0][0]);
Note that you are still accessing arr1 and arr2 through pFoo2, indirectly. You must still respect their lengths, even though those lengths are not the same as each other, and are not evident from the type of pFoo2 or of foo.
I have been researching and testing my knowledge in C (I am a new computer engineering student), and ran into a problem I cannot figure out.
When trying to pass a 2D array to a function, I learned that you cannot do so with dynamically allocated arrays, since the compiler needs to know array[][columns]. However, I learned that a 2D array is stored a 1D array, where the elements of each new row just follows the elements of the previous row. When I pass an array name to a function as a pointer to an array, this seems to be the case, and my code works fine. However, in the function where the 2D array is declared, it behaves as an array of pointers instead.
#include <stdio.h>
void printArray(int *A, int* dimA) {
for(int i = 0; i < dimA[0]; ++i) {
for(int j = 0; j < dimA[1]; ++j) {
printf("%3d", A[i*dimA[1] + j]);//This would work if the elements of A[] are the rows of a 2D array mapped into a 1D array
}
printf("\n\n");
}
return;
}
int main(){
int A[2][2] = {{1,2},{3,4}};
int dimA[2] = {2,2};//dimensions of the array
int i, j;
for(i = 0; i < dimA[0]; ++i) {
for(j = 0; j < dimA[1]; ++j) {
printf("%3d", *(A[i] + j)); //This would work if the elements of A[] are pointers
}
printf("\n\n");
}
for(i = 0; i < dimA[0]; ++i) { //Same code as printArray function
for(j = 0; j < dimA[1]; ++j) {
printf("%3d", A[i*dimA[1] + j]);//This would work if the elements of A[] are the rows of a 2D array mapped into a 1D array
}
printf("\n\n");
}
printArray(A, dimA);
return 0;
}
The following code outputs the array correctly in main() when the array is treated as an array of pointers, but not when treated as a 1D array of integers. However, when I pass the same array to the printArray function as a pointer, I can treat it as a 1D array of integers and it works. Any help would be appreciated (I already understand that I can instead use an array of pointers, but I really want to understand what the problem was). Thanks!
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.
Thus in the first for loop
for(i = 0; i < dimA[0]; ++i) {
for(j = 0; j < dimA[1]; ++j) {
printf("%3d", *(A[i] + j)); //This would work if the elements of A[] are pointers
}
printf("\n\n");
}
the expression A[i] has the type int[2]. Being converted to pointer it has the type int *. So for each i the expression A[i] points to the first element of each "row" of the array A.
The expression A[i] + j points to the j-th element of each row. So dereferencing the pointer you get j-th element of the i-th row of the array.
In the second loop
for(i = 0; i < dimA[0]; ++i) { //Same code as printArray function
for(j = 0; j < dimA[1]; ++j) {
printf("%3d", A[i*dimA[1] + j]);//This would work if the elements of A[] are the rows of a 2D array mapped into a 1D array
}
printf("\n\n");
}
the expression A[i*dimA[1] + j] has the type int * and points to i *dimA[1] + j "row" of the array that is it points beyond the array. So the loop does not make sense.
The function declared like
void printArray(int *A, int* dimA);
is called like
printArray(A, dimA);
The second argument that has the type int[2] is indeed converted to pointer of the type int * that points to the first element of the array.
As for the first argument then it is also converted to pointer to its first element. And what is the element of the array? The element of this two-dimensional array is a one-dimensional array of the type int[2]. So pointer to an object of this type will have type int ( * )[2]
Pointers int * and int ( * )[2] are not compatible and by this reason the compiler shall issue a diagnostic message.
the correct declaration of the function should look like
void printArray(int ( *A )[2], int *dimA);
When trying to pass a 2D array to a function, I learned that you cannot do so with dynamically allocated arrays, since the compiler needs to know array[][columns].
This is true, in the sense that you cannot pass any array to a function. You cannot even express such a concept in C, though you can write code that looks like that to the casual eye. In almost every context where an expression evaluating to an array appears -- including function call expressions -- the array value is replaced by a pointer to the first array element.
It is partially true in the sense that a 2D array is an array of arrays, and the dimension of the (array) element type is is part of the overall array's type, part of the type of every element, and part of the type of a pointer to the first element. As such, that dimension must be part of the type of any function parameter to which you want to pass (a pointer to the first element of) the array.
It is most accurately characterized as false, however, even for 2D arrays both of whose dimensions are determined at run time. Since 1999, C has supported variable-length arrays (though in C11 it was made optional), and these play very nicely indeed with dynamically-allocated multi-dimensional arrays and with pointers to arrays of varying dimension:
// Dynamically allocating a 2D array of runtime-determined dimensions:
unsigned rows = calculate_number_of_rows();
unsigned columns = calculate_number_of_columns();
int (*matrix)[columns] = malloc(rows * sizeof(*matrix));
They work well for functions accepting such pointers, too:
void do_something(unsigned rows, unsigned columns, int matrix[rows][columns]);
... or, equivalently ...
void do_something(unsigned rows, unsigned columns, int matrix[][columns]);
... or ...
void do_something(unsigned rows, unsigned columns, int (*matrix)[columns]);
Those three forms are completely equivalent.
However, I learned that a 2D array is stored a 1D array, where the elements of each new row just follows the elements of the previous row.
A 2D array is an array of 1D arrays. The elements of any array are stored contiguously in memory without padding, so the layout of a 2D array of dimensions (r, c) cannot be distinguished from the layout of a 1D array of dimension r * c, but I recommend against thinking of it in the terms
you used.
When I pass an array name to a function as a pointer to an array, this seems to be the case, and my code works fine.
Do not do that. In practice, it is very likely to work exactly as you say, but you should heed the warnings emitted by your compiler -- and it definitely should be emitting warnings about that.
However, in the function where the 2D array is declared, it behaves as an array of pointers instead.
You've not presented an example of a function that would fit your description. Certainly it is possible to pass an array of pointers, but it is quite possible to pass a pointer to an array instead. See above for examples.
Compiling the code gives a warning that is a bit of a clue to what is going on:
main.c:27:27: warning: format specifies type 'int' but the argument has type 'int *' [-Wformat]
printf("%3d", A[i*dimA[1] + j]);//This would work if the elements of A[] are the rows of a 2D array mapped into a 1D array
~~~ ^~~~~~~~~~~~~~~~
main.c:32:16: warning: incompatible pointer types passing 'int [2][2]' to parameter of type 'int *' [-Wincompatible-pointer-types]
printArray(A, dimA);
^
main.c:3:22: note: passing argument to parameter 'A' here
void printArray(int *A, int* dimA) {
When you declare your array:
int A[2][2] = {{1,2},{3,4}};
this is stored as one contiguous chunk of memory, as you stated. In memory, this is equivalent to:
int A[4] = {1,2,3,4};
However, whenever you go to lookup/dereference the values, depending on the type, the compiler is implicitly doing some bookkeeping for you. For the second case:
int A[4] = {1,2,3,4};
A[0] = *(&A + 0) = 1
A[1] = *(&A + 1) = 2
...
fairly straightforward, the index is simply an offset off the base address. However, for the first case:
y x
int A[2][2] = {{1,2},{3,4}};
y x
A[0][0] = *(&A + 2 * 0 + 0) = *(&A + 0) = 1
A[1][0] = *(&A + 2 * 1 + 0) = *(&A + 2) = 3
...
things start to look a bit confusing.
The first thing to note is that since the type is declared as an int[2][2], you must dereference it twice. That is what the first warning is complaining about. Because it was only dereferenced once, your int ** became an int *, which is not the same as an int.
The second thing to notice is because the type is declared as a multi-dimensional array, the compiler will do some bookkeeping for you. Since the array was being dereferenced on the first dimension, the size of the second dimension to stride to the correct location was already taken into account, so instead of col * j + i, you actually got col * (col * j + i) + i, which is not what you want!
To get the desired effect, you can either:
Cast A into an int *. This is what happened when you called your printArray function, and also why it works.
Access the array from the lowest dimension. Instead of saying A[i*dimA[1] + j], do A[0][i*dimA[1] + j]. This will correctly dereference to an int and also effectively bypass the bookkeeping.
I am trying to get value from "second row" in multidimensional array. But I have some problems with that.
I thought that numbers are stored sequentialy in memory so tab[2][2] is stored same as tab[4]. But it seems that I was wrong.
This is what I tried:
int b[2][2] = {{111,222},{333,444}};
int i = 0;
for(;i < 100; i++)
printf("%d \n", **(b+i));
The problem is that I get only 111 and 333 as the result. There is no 222 or 444 in other 98 results. Why?
The problem is that **(b+i) doesn't do what you think it does. It evaluates to:
b[i][0]
As Matt McNabb noted,
**(b+i)
is equivalent to:
*(*(b+i)+0)
and since *(b+i) is equivalent to b[i], the expression as a whole can be seen as:
*(b[i]+0)
and hence:
b[i][0]
Since your array has only 2 rows, only the values of i for 0 and 1 are in bounds of the array (that's the 111 and 333). The rest was wildly out of bounds.
What you could do is:
#include <stdio.h>
int main(void)
{
int b[2][2] = { { 111, 222 }, { 333, 444 } };
int *base = &b[0][0];
for (int i = 0; i < 4; i++)
printf("%d: %d\n", i, base[i]);
return 0;
}
Output:
0: 111
1: 222
2: 333
3: 444
You can think of a two-dimensional array as a special form of a one-dimensional array. Your array holds 2 elements (!). (Each element happens to be an array of two elements, but let's ignore that for a sec.) Let's make the dimensions different so that we can distinguish them:
int arr2d[2][3] holds 2 elements (each being an array of 3 elements). The "primary index" is written first, that is if you have a one-dimensional array of 3 like int arr1d[3]and want to have an array of those with three elements like arr2d, you have to write arr2d[2][3]. You could arrange that with a typedef which makes it clear that all arrays in C are essentially 1-dimensional:
typedef int arr3IntT[3];
arr3IntT arr2d[2] = { {0,1,2}, {3,4,5} };
Now what does arr2d+i mean? arr2d, like any array, decays to a pointer to its first element (which is an array of 3 ints). arr2d+1 adds the offset of 1 of those elements to the address, so that like always the expression yields the address of the second element (which is the second array of 3 ints). Dereferencing it once like *(arr2d+1) yields that element, i.e. the one-dimensional sub-array of 3 ints. It decays to a pointer to its first element, i.e. a pointer to the first int in that second sub-array. Dereferencing that in the expression **(arr2d+1) yields that int, like always. To summarize: In your original code you iterate from sub-array to sub-array, always referencing the first of their elements, incidentally going out of bounds for i>1.
But principally you are right, elements of n-dimensional arrays in C are lying contiguously in memory, so you can access them one by one if you like to. You just have to index a pointer to int, not one to int[3]. Here is how: The expression arr2d decays to a pointer to its first element which is an array of 3 ints. Dereferencing that gives that first element, a one-dimensional array of 3 ints. Like all arrays, it decays to a pointer to its first element, an int, which is the very first element in the data:
#include<stdio.h>
int main()
{
int arr2d[2][3] = { {0,1,2}, {3,4,5} };
int *p_el1 = *arr2d;
int i, j;
// Sanity check by indexing 2-dimensionally
for(i=0; i<2; i++) for(j=0; j<3; j++) printf("%d\n", arr2d[i][j]);
// iterate the elements 1 by 1
for(i=0; i<3*2; i++) printf("%d\n", p_el1[i]);
}
A multidimensional array is not a fundamentally new type. It's an array type where the elements are themselves arrays. To quote the C99 standard §6.2.5 ¶20 (Types)
An array type describes a contiguously allocated nonempty set of
objects with a particular member object type, called the element type.
Array types are characterized by their element type and by the number
of elements in the array.
int b[2][2] = {{111, 222}, {333, 444}};
The above statement defines b to be an array of 2 elements where each element is the type int[2] - an array of 2 integers. It also initializes the array with the array initializer list. An array is implicitly converted to a pointer to its first element in some cases.
In the printf call, b decays to a pointer to its first element. Therefore, it's equivalent to &b[0] and has type int (*)[2] - a pointer to an array of 2 integers. Please note that it's undefined behaviour to access elements out of the bound of an array. Therefore, the for loop condition i < 100 is wrong. It should be i < 2. Now, let's try to demystify the expression **(b+i).
b -------------> pointer to a[0]
b + i ---------> pointer to a[i]
*(b + i) -----> a[i]
*(*(b + i)) ---> *(a[i]) ----> *(&(a[i])[0]) ----> a[i][0]
As noted, the elements of the array are themselves arrays. Therefore, a[i] is an array. Again, the array decays to a pointer to its first element, i.e., to &(a[i])[0]. Applying indirection operator * on this pointer gets us the value at that address which is a[i][0].
You can access the elements of the array through a pointer but the pointer type must be a pointer to element type of the array.
#include <stdio.h>
int main(void) {
int b[2][2] = {{111, 222}, {333, 444}};
int (*p)[2] = b;
// (sizeof b / sizeof b[0]) evaluates to
// the size of the array b
for(int i = 0; i < (sizeof b / sizeof b[0]); i++)
// (sizeof *p / sizeof (*p)[0]) evaluates to
// the size of element of the array which is
// itself an array.
for(int j = 0; j < (sizeof *p / sizeof (*p)[0]); j++)
printf("%d\n", *(*(p + i) + j));
return 0;
}
Here, the expression *(*(p + i) + j) can be decoded as
p ---------------> pointer to the first element of b, i.e., &b[0]
(p + i) ----------> pointer to b[i], i.e., &b[i]
*(p + i) ---------> the array element b[i] ---> decays to &(b[i])[0]
*(p + i) + j -----> &(b[i])[j]
*(*(p + i) + j) --> the element b[i][j]
Therefore, the expression *(*(p + i) + j) is equivalent to b[i][j]. In fact, the C99 standard §6.5.2.1 ¶2 says -
The definition of the subscript operator [] is that E1[E2] is
identical to (*((E1)+(E2)))
This means we have the following are equivalent with context to the above program -
*(*(p + i) + j)
// equivalent to
p[i][j]
// equivalent to
b[i][j]
// equivalent to
*(*(b + i) + j)