Here's a way to think of your points[3][4]
array. It is an array of 3 rows and 4 columns. The rows are numbered 0, 1, and 2, and the columns are numbered 0, 1, 2, and 3.
Let's populate the array with values, to help visualize it.
0 1 2 3
-- --
0 | 11 12 13 14 |
1 | 21 22 23 24 |
2 | 31 32 33 34 |
-- --
The twelve values are arranged in memory in the following order. Each row is placed contiguously after the previous row:
11 12 13 14 21 22 23 24 31 32 33 34
Even though the variable is defined as a 2-dimensional array, it appears in memory as though it is a 1-dimensional array of 12 values.
A double occupies 8 contiguous bytes. Therefore, the address of each array element is 8 higher than the previous address. So if the array starts at address 0x0012F5A4, here are the addresses of all of the elements.
Address Value
------- -----
0012F5A4 11.0
0012F5AC 12.0
0012F5B4 13.0
0012F5BC 14.0
0012F5C4 21.0
0012F5CC 22.0
0012F5D4 23.0
0012F5DC 24.0
0012F5E4 31.0
0012F5EC 32.0
0012F5F4 33.0
0012F5FC 34.0
To emphasize that these are double
values, I added the ".0" to each one in the above table, For the remainder of this post, I'll omit the ".0" when referring to the values.
Now let's take each case one by one. I'll explain only the first seven cases in your question.
Case 1
double *pd0 = points[0][1];
//-> error: incompatible type (cannot convert 'double' to 'double*' in initialization)
Here, pd0
is declared as a pointer to a double. Pointer variables contain the addresses of other variables.
On the right side of the assignment statement, points[0][1]
contains the double value 12
in my example array.
The error occurs because you are trying to assign a double value to a pointer variable.
Case 2
double *pd1 = &points[0][1];
//-> compatible type
This is fine because the &
is the referencing operator. It provides the address of the variable it precedes. So &points[0][1]
is the address of the double value stored at points[0][1]
. In my example array, this address is 0x0012F5AC. The pointer pd1
is therefore being assigned the address 0x0012F5AC.
Case 3
double *pd2 = &points[5][2];
//-> compatible type (possible runtime error; points to the element out of bound)
If your compiler is smart enough to implement runtime array-bounds checking, then this might produce an exception when the statement executes since the array was not defined to have a row [5]. Pointer pd2
would be assigned the address of a hypothetical element well beyond the end of the 12-element array. Even if an exception doesn't occur, if the data at that address is used, it will most certainly not produce a predictable result, and might result in an eventual crash of the program.
Case 4
double *pd3 = &**points;
//-> compatible type
To understand this, let's add some parentheses to the right hand side, to emphasize the order in which the operators are applied.
double *pd3 = &(*(*points));
Now working from the inside out,
- The term
points
identifies the entire 2-dimensional array.
- The term
(*points)
identifies the address of the first row in the array.
- The term
(*(*points))
(or **points
) identifies the value of the first element in the first row of the array.
- The term
&(*(*points))
(or &**points
) identifies the address of that element.
So we are assigning the address of that single element to the pointer variable pd3
.
Case 5
double (*p4d0)[4] = points[0];
//-> error: incompatible type (cannot convert 'double*' to 'double (*)[4]' in initialization)
For this case, I'll refer you to the answer given by John Bode in this post. Bode says,
When an array expression appears in most contexts, its type is
implicitly converted from "N-element array of T" to "pointer to T",
and its value is set to point to the first element in the array. The
exceptions to this rule are when the array expression is an operand of
either the sizeof
or address-of (&
) operators, or when the array is a
string literal being used as an initializer in a declaration.
So points[0]
is implicitly converted from "4-element array of double" to "pointer to double".
p4d1
is declared expressly as a pointer to an array of 4 doubles. The incompatibility arises because "pointer to double" is not the same type as "pointer to an array of 4 doubles".
Case 6
double (*p4d1)[4] = &points[0];
//-> compatible type
As in Case 5, p4d1
is a pointer to an array of 4 doubles.
&points[0]
provides the address of the 0th row. Recall that in the excerpt from John Bode's post, he said that an exception to the rule converting an array expression to a pointer occurs when using the address-of (&
) operator. So &points[0]
is also a pointer to an array of 4 doubles. After executing the above statement, p4d1
contains that address.
Case 7
double (*p4d2)[4] = (double (*)[4])points[0];
//-> compatible type
This solves the problem of Case 5. It is casting points[0]
to the same type as p4d2
, effectively undoing the implicit conversion to "pointer to double".
I'll leave the remaining cases for you to sort out.