What have you done here:
float **m = (float**)calloc(n, sizeof(float*));
- You have created a pointer of type float**.
Then you assign this pointer the initial address of a sequential block of memory consisting of
(n * sizeof(float*))
bytes. All these bytes have got the value of zero in them. You can address n elements now from this memory block, each holding
sizeof(float*)
bytes, i.e:
m[0], m[1], m[2], ... , m[n - 1]
where m[0] will return you the actual value of the first element from the memory block, which is a memory address.
The next thing you do is: you take each of these elements (m[0], m[1], etc.) and assign to each of them new different memory blocks at different memory locations. Each of these memory blocks consist of
(n * sizeof(float*))
bytes and you can address n elements, where each element consists of
sizeof(float)
bytes, i.e:
(row 0) m[0][0], m[0][1], m[0][2], . . ., m[0][n - 1]
(row 1) m[1][0], m[1][1], m[1][2], . . ., m[1][n - 1]
.
.
.
(row n - 1) m[n - 1][0], m[n - 1][1], m[n - 1][2], . . ., m[n - 1][n - 1]
So overall, what you get is:
An array, m, that holds n consecutive memory addresses where each of those memory addresses point to n memory blocks all located at different addresses. Each of them hold
(n * sizeof(float))
bytes, all consecutive.
You cannot say that the next memory location after m[0][n - 1] is m[1][0], but you can say that the next memory location after m[0][n - 2] is m[0][n - 1]