A pointer and an array are different things.
A pointer to a type holds the address where a variable of declared type is allocated.
An array of a type is a memory space where variables of the declared type are contiguously stored.
Now you're declaration. There is no ambiguity (even if is not so evident) in C declarations.
In case A you have declared:
int **M1;
This is not a 2d array, not even a monodimensional one. You are declaring a variable, M!
, as a pointer to another pointer to an int. In plane word M1
will hold the address of another variable that in turn holds the address of where in memory is stored an integer.
Now executing:
M1 = (int **)malloc(m * sizeof(int *));
You assign to M1
the address of a memory area that can store up to m contiguous integer pointers, an access to memory pointed by M1
, and successive locations acts as an array access (but is not). This is more or less equivalent to the static declaration:
int *[m]; //an array of pointers to int
Then assigning each element of this pseudo-array a memory storage for d
contiguous integers:
for (int i=0; i<m; i++)
{
M1[i] = (int *)malloc(d * sizeof(int));
}
you now have room to store d
consecutive integers that begins at addresses saved in M1[i]
for i=0->d-1.
What happens when you try to access a value using subscripts: M1[a][b]
?
The compiler retrieve the address pointed by M1
and using the first subscript (a
), retrieves the contents pointed by the address in the atht position of the pointers array. This points to the first integer of the subspace that we have allocated to hold d
consecutive int's. This is indeed a monodimensional array of int. Applying the second subscript to it the compiler finally retrieve the required integer. Cose to a bidimensional array addressing, but no banana! It isn't a bidimensional array of int's. :-)
In case B you are declaring a pointer to a monodimensional array of int, to which you are assigning enough space to hold your bidimensional array. Because in C doesn't exist the concept of multidimensional array, but a basic principle of array of array of array.... etc. (ad-libitum), the declaration:
int (*M1)[m] = malloc(sizeof(int[m][d]));
as a pointer to modimensiional array with space allocated for an array of [m][d]
elements made the trick.
But of course both the solutions are wrong!
You are using side effects to obtain what you want, but using surrogates of what you declared to need: a bidimensional array of int's.
The correct solution is to define a pointer to a bidimensional array of integers where only the first subscript is required:
int (*M1)[][m]; //declare a pointer to a real array
M1 = malloc(m * d * sizeof(int)); //Allocate room for bidimensional array
for (int i=0; i<m; i++)
for (int j=0; j<d; j++)
{
(*M1)[i][j] = (i*100)+j; //Fill elements with something using bidimensional subscripting
}
for (int i=0; i<m; i++)
for (int j=0; j<d; j++)
{
printf("[%d][%d]=%d\n", i, j, (*M1)[i][j]); //Check back data...
}
Now have a look to what happens if you go out bounds in the 3 cases.
In case A if you get out of bounds with the first subscript you'll collect a wrong pointer that will cause immediately a memory fault, in case B you will have memory fault only if you go out of process addressable memory, the same for the correct solution.
This should answer to your question.
Last, because we are talking of misunderstandigs about arrays and pointers, don't misinterpret the standard ISO 9899:2011§6.7.6.3/7 that says:
A declaration of a parameter as ‘‘array of type’’ shall be adjusted to
‘‘qualified pointer to type’’, where the type qualifiers (if any) are
those specified within the [ and ] of the array type derivation. If
the keyword static also appears within the [ and ] of the array type
derivation, then for each call to the function, the value of the
corresponding actual argument shall provide access to the first
element of an array with at least as many elements as specified by the
size expression.
It simply states that arrays will be passed only by reference (automatically adjusted by compiler), never by value (that is legal for structs ie). You are not requested to supply any qualified pointer instead of an array in function call or the program will crash (A declaration of a parameter as ‘‘array of type’’ shall be adjusted to
‘‘qualified pointer to type’’ - means that will be adjusted by compiler, not by you). Cases as char *[]
and char **
works for the reason reported above, not because it is legal to exchange them!