Why are the addresses of malloced 2D array not what I expect? - arrays

With the aim of allocating memory for a 2D array in C I run
double (*t)[2] = malloc(sizeof(*t)*4);
I expected this to allocate 64 bytes (4 times the size of two doubles). That is, I expected t[0] and t[1] to have room for 4 doubles each. For this reason, I thought that the address of t[0] and t[1] should be separated by 32 bytes. However, I find that they are separated by 16 bytes (corresponding to only two doubles).
printf("Address of t[0]: %p\n", t[0]); // Address of t[0]: 00C92490
printf("Address of t[1]: %p\n", t[1]); // Address of t[1]: 00C924A0
What am I missing here?

It seems to be a misunderstanding in the declaration and the allocation here. You declare t to be an array, where each element in the array t is an array of two double elements. Then you allocate enough space for four elements in t.
Your definition and allocation is equivalent to:
double t[4][2];
If you want to create an array of two elements, where each element is a dynamically allocated array, you need an array of pointers instead:
double *t[2] = { malloc(sizeof(*t[0]) * 4), NULL };
The above define t as an array of two elements, each elements is a pointer to a double, and you initialize the first element (t[0]) to point to an array of four double elements, and the second (t[1]) to be a null pointer.
The important part is the difference between
double (*t)[2];`
and
double *t[2];
Using the clockwise/spiral rule the first defines t as "a pointer to an array of two double". The second defines t as "an array of two pointers to double".

I expected this to allocate 64 bytes (4 times the size of two doubles)
This your expectation is correct. But this your conclusion
That is, I expected t[0] and t[1] to have room for 4 doubles each. For
this reason,
is wrong.
This line
double (*t)[2] = malloc(sizeof(*t)*4);
is equivalent to
double (*t)[2] = malloc( sizeof( double[4][2] ) );
because The type of the expression *t is double[2].
So t[0] and t[1] have the type array type double[2]. sizeof( double[2] ) is equal to 16.
Here is a demonstration program.
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
double( *t )[2] = malloc( sizeof( double[4][2] ) );
printf( "sizeof( double[2] ) = %zu\n", sizeof( *t ) );
printf( "Address of t[0]: %p\n", ( void * )t[0] );
printf( "Address of t[1]: %p\n", ( void * )t[1] );
printf( "(char * )t[0] + sizeof( double[2] ) = %p\n",
( void * )( ( char * )t[0] + sizeof( double[2] ) ) );
}
The program output might look like
sizeof( double[2] ) = 16
Address of t[0]: 00B971D8
Address of t[1]: 00B971E8
(char * )t[0] + sizeof( double[2] ) = 00B971E8

double (*t)[2] = malloc( sizeof *t * 4 );
creates t as a 4x2 array of double, not a 2x4 array. The result looks like:
+---+ +---+
t: | | -----> | | t[0][0]
+---+ + - +
| | t[0][1]
+---+
| | t[1][0]
+ - +
| | t[1][1]
+---+
| | t[2][0]
+ - +
| | t[2][1]
+---+
| | t[3][0]
+ - +
| | t[3][1]
+---+
hence why the addresses of t[0] and t[1] are 16 (2 * sizeof (double)) bytes apart.
If you want to dynamically allocate a 2x4 array of double, you need to write
double (*t)[4] = malloc( sizeof *t * 2 );
IOW,
T (*p)[COLUMNS] = malloc( sizeof *p * ROWS );
You're allocating enough space for ROWS instances of T [COLUMNS].

Related

Different ways Asterisk (* operator) is used in C

So I know that the * operator is used for multiplication and as a pointer but sometimes I see the * operator and I am not sure what it means. Have a look at this code which defines a matrix
#ifndef MATRIX_H
#define MATRIX_H
/* The data structure representing a matrix */
typedef struct {
int rows; /* number of rows */
int columns; /* nuumber of columns */
int **content; /* matrix elements in a two dimensional array */
} matrix_t;
what does a double asterisk used for here?
I assume ** operator is used for pointers in 2d arrays but I am not sure.
Multiple indirection is a thing - you can have pointers to pointers, pointers to pointers to pointers, pointers to arrays of pointers, arrays of pointers to pointers, pointers to arrays of pointers to pointers to arrays of pointers to functions returning pointers, etc.
As used here, the intent is that you allocate an array of pointers:
matrix_t mat;
mat.content = calloc( 2, sizeof *mat.content );
which gives us
int ** int *
+---+ +---+
mat.content: | | ---> | | mat.content[0]
+---+ +---+
| | mat.content[1]
+---+
and then for each of those pointers, you allocate an array of int:
for ( size_t i = 0; i < rows; i++ )
mat.content[i] = calloc( 2, sizeof *mat.content[i] );
giving us
int ** int * int
+---+ +---+ +---+
mat.content: | | ---> | | mat.content[0] ---> | | mat.content[0][0]
+---+ +---+ +---+
| | mat.content[1] -+ | | mat.content[0][1]
+---+ | +---+
|
|
| +---+
+-> | | mat.content[1][0]
+---+
| | mat.content[1][1]
+---+
The expression mat.content has type int **, so the expression *mat.content has type int *; thus, sizeof *mat.content == sizeof (int *). Similarly, the expression mat.content[i] also has type int * (*p == p[0]), so *mat.content[i] has type int, so sizeof *mat.content[i] == sizeof (int).
Because of how array subscripting works, mat.content[i][j] works exactly the same way as if you had declared mat.content as a regular 2D array of int. It's just instead of all the rows being allocated in a single contiguous chunk, they're allocated piecemeal.
General rules:
T *p; // p is a pointer to T
T *ap[N]; // ap is an array of pointer to T
T (*pa)[N]; // pa is a pointer to an array of T
T *fp(); // fp is a function that returns pointer to T
T (*pf)(); // pf is a pointer to a function that returns T
T **p; // p is a pointer to a pointer to T
T ***p; // p is a pointer to a pointer to a pointer to T
const T *p; // p is a pointer to const T - you can write to p, but not *p
T const *p; // same as above
T * const p; // p is a const pointer to T - you can write to *p, but not to p
When used in an expression, the * "dereferences" the pointer - if you have a declaration like
int x;
int *p = &x;
the expression *p acts as kind of an alias for x:
*p = 10; // does the same thing as x = 10;
The object p stores the location of x, and the type of the expression p is int * (pointer to int). The expression *p is equivalent to the expression x - not only does it yield the value stored in x, you can write a new value to x through *p as shown above.
The declarations above give us this relationship:
p == &x // int * == int *
*p == x // int == int
The array subscript expression a[i] is defined as *(a + i) - given a starting address designated by a, offset i elements (not bytes!) from that address and dereference the result.
*p == *(p + 0) == p[0]
Arrays are not pointers, nor do they store a pointer to their first element. Unless it is the operand of the sizeof, _Alignof, or 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. This is important - if you declare an array
int a[10];
a does not store an address to its first element; instead, any time the compiler sees the expression a outside of the contexts listed above, it will basically replace it with something equivalent to &a[0].

addressing with pointer in c languagge

I'm studying pointers in C language with Ted Jensen's "A TUTORIAL ON POINTERS AND ARRAYS IN C" manual. It's a very prickly argument.
Question 1. I have come to program 3.1 and would like a clarification. This is the program:
#include <stdio.h>
char strA[80] = "A string to be used for demonstration purposes";
char strB[80];
int main(void) {
char *pA;
char *pB;
puts(strA);
pA = strA;
puts(pA);
pB = strB;
putchar('\n');
while(*pA != '\0')
{
*pB++ = *pA++;
}
*pB = '\0';
puts(strB);
return 0;
}
Regarding the line pA = strA;, the book says "We then [point] the pointer pA at strA. That is, by means of the assignment statement we copy the address of strA [0] into our variable pA", which is what I don't understand.
To copy the address of strA[0] into our variable pA via the assignment declaration, shouldn't we write pA = & strA?
Question 2. The expression c = * ++ p; increases the value of p or the address of p?
Does the indirection operator (*) not indicate the value of the pointed variable?
To copy the address of strA [0] into our variable pA via the assignment declaration, shouldn't we write pA = & strA?
&strA is the address of strA. &strA[0] is the address of strA[0]. These are the “same” in the sense they point to the same place in memory, but they have different types, and the compiler would complain if we wrote pA = &strA when the type of pA is a pointer to the type of the elements of strA.
When we write pA = strA, the array strA is automatically converted to a pointer to its first element, so pA = strA is equivalent to pA = &strA[0].
Question 2: the expression c = * ++ p; increases the value of p or the address of p?
The C grammar organizes this as c = *(++p);, and ++p increases the value of p. If p is a pointer, it increases the value of that pointer. The * operator uses the increased value.
Be careful about speaking of the address of a pointer. The value of the pointer is an address, but you should not say that is the address of the pointer. The pointer is itself an object in memory, and so it has an address in memory where its value is stored. The address where a pointer is stored is different from the address stored in it. The “address of a pointer” is the address where the pointer is stored, not the value of the pointer.
It's simply because strA, which is char array, is a pointer.
An array's "value" is actually nothing more than the address of the first element of array.
When you assign address of primitive data type(int, char, etc..), you should assign it in the way you described.
int x;
int *pA;
pA = &x;
When you assign address of pointer data type(array, etc..), you should assign it without & operator, since the value is in itself the address.
int x[10];
int* pA;
pA = x;
Original code
#include <stdio.h>
char strA[80] = "A string to be used for demonstration purposes";
char strB[80];
int main(void)
{
char *pA; /* a pointer to type character */
char *pB; /* another pointer to type character */
puts(strA); /* show string A */
pA = strA; /* point pA at string A */
puts(pA); /* show what pA is pointing to */
pB = strB; /* point pB at string B */
putchar('\n'); /* move down one line on the screen */
while(*pA != '\0') /* line A (see text) */
{
*pB++ = *pA++; /* line B (see text) */
}
*pB = '\0'; /* line C (see text) */
puts(strB); /* show strB on screen */
return 0;
}
In C, when you write :
char strA[80];
Memory is allocated for your table. This is an example for you to try and visualize what it looks like.
[0] 1st Element
[1] 2nd Element
[2] 3rd Element
[....] ....
[n] nth Element
0000
0001
0002
0003
n
strA is a pointer to the address where your table starts in the memory (0000 in our example), which is the same as the address of its first element strA[0].
So when you write
pA = strA
you are actually copying the first element's address (0000 in our example) to pA
To copy the address of strA [0] into our variable pA via the assignment declaration, shouldn't we write pA = & strA?
Arrays are weird and don't behave like other types.
The expression strA "decays" from type "N-element array of char" to "pointer to char", and the value of the expression is the address of the first element. This "decay" doesn't happen when the array expression is the operand of the sizeof or unary & operators, so the expression &strA has type "pointer to N-element array of char", or char (*)[N], which is not the same as char *. The address value is the same (the address of an array is the same as the address of its first element), but the types are different.
Assuming the declarations
char str[N]; // for some size N
char *p;
when you write
p = str; // char * = char *
the expression str has type "N-element array of char"; however, since str is not the operand of the sizeof or unary & operators, it "decays" to type char * and evaluates to the address of the first element. It’s equivalent to writing
p = &str[0]; // char * = char *
There is a reason for this - C was derived from an earlier language named B, and in B array types had a dedicated pointer to the first element - when you declared an array in B like
auto a[N];
what you got in memory was
+---+
a: | | -------+
+---+ |
... |
+---+ |
| | a[0] <-+
+---+
| | a[1]
+---+
...
and the array subscript operation a[i] was defined as *(a + i) - given the starting address a, offset i elements from that address and dereference the result.
When he was designing C, Ritchie wanted to keep B's array behavior, but he didn't want to keep the explicit pointer that behavior required. When you declare an array in C like
int a[N];
what you get in memory is
+---+
a: | | a[0]
+---+
| | a[1]
+---+
...
Instead of setting aside space for an explicit pointer to the first element, the compiler replaces any occurrences of the expression a with a pointer to a[0]. So a[i] is still defined as *(a + i). Unfortunately, this means that arrays lose their "array-ness" under most circumstances and most of the time what you're dealing with is a pointer.
Question 2: the expression c = * ++ p; increases the value of p or the address of p?
This gets a little complicated. To answer as asked, it increases the value of p - it sets p to point to the next object in a sequence. This is probably better explained with a concrete example.
Assume the following declarations:
char str[] = "foo";
char *p = str;
then the following are true:
+---+
str: |'f'| str[0] <--- p
+---+
|'o'| str[1] <--- p + 1
+---+
|'o'| str[2] <--- p + 2
+---+
| 0 | str[3] <--- p + 3
+---+
p == &str[0] // char * == char *
*p == str[0] == 'f' // char == char == char
p + 1 == &str[1]
*(p + 1) == str[1] == 'o'
p + 2 == &str[2]
*(p + 2) == str[2] == 'o'
The result of the expression p + 1 is a pointer to str[1], the result of the expression p + 2 is a pointer to str[2], etc.
c = *++p; is roughly equivalent to writing
tmp = p + 1;
c = *tmp;
p = p + 1;
with the caveat that the updates to c and p can happen in any order - they may even be evaluated simultaneously (either interleaved or in parallel).
In this case, it means we assign the value of *(p + 1) ('o') to c, and update p to point to str[1], leaving us with this:
+---+
str: |'f'| str[0]
+---+
|'o'| str[1] <--- p
+---+
|'o'| str[2] <--- p + 1
+---+
| 0 | str[3] <--- p + 2
+---+
p == &str[1]
*p == str[1] == 'o'
p + 1 == &str[2]
*(p + 1) == str[2] == 'o'
p + 2 == &str[3]
*(p + 2) == str[3] == 0

how to dynamically declare a 2-d array in c using type: int(*)[ ]?

I have tried using int**(pointer to a pointer) and int*[](array of pointers) but i am unable to perform the task with pointer to an array.How can we use pointer to an array and malloc function to create an 2-d array in c.
Please provide a sample code:)
void *allocateMemory2DArray(size_t x, size_t y)
{
int (*array)[x][y];
array = malloc(sizeof(*array));
return array;
}
5d array
void *allocateMemory5DArray(size_t x, size_t y, size_t v, size_t q, size_t r)
{
int (*array)[x][y][z][q][r];
array = malloc(sizeof(*array));
return array;
}
So let’s start from the ground up.
The typical way to dynamically allocate an N-element array of T (for some arbitrary T) is
T *p = malloc( sizeof *p * N ); // sizeof *p == sizeof (T)
This allocates enough space for N objects of type T, with p pointing to the first element
T * T
+–––+ +–––+
p: | |–––––>| | p[0]
+–––+ +–––+
| | p[1]
+–––+
...
+–––+
| | p[N-1]
+–––+
Now, replace T with an array type - R [M]:
R (*p)[M] = malloc( sizeof *p * N );
This allocates enough space for N objects of type R [M] - IOW you’ve allocated an NxM array of R. p still points to the first element, it’s just that in this case the first element is an array in itself.
R (*)[M] R [M]
+–––+ +–––+
p: | |––––––>| | p[0][0]
+–––+ + - +
| | p[0][1]
+ - +
...
+ – +
| | p[0][M-1]
+––—+
| | p[1][0]
+ – +
| | p[1][1]
+ - +
...
+ – +
| | p[1][M-1]
+–––+
...
+ – +
| | p[N-1][M-1]
+–––+
You’d index into p like any regular 2d array.
You can extend this to higher-dimensioned arrays - replace R with another array type like Q [L]:
Q (*p)[M][L] = malloc( sizeof *p * N );
This allocates space for an NxMxL array, and again you’d index into it like any 3d array p[i][j][k].
The semantics are exactly the same in every case - you’re allocating space for N objects, it’s just that the types of the objects are different.

allocate memory for 2d array c

I've been reading a lot of the posts about allocating memory, and I think that I understand the concept, but I have been told that I have to use an approach that looks something like this:
double ** malloc_array2d(size_t m, size_t n)
{
double **A;
size_t i;
A = malloc(m * sizeof(double *));
if (A == NULL)
return NULL;
A[0] = (double *) malloc(m * n * sizeof(double));
if (A[0] == NULL)
{
free(A);
return NULL;
}
for (i = 1 ; i < m ; i++)
A[i] = A[0] + i *n;
return A;
}
And then of course I will have to free the memory later - but I just don't quite understand this kind of approach and more specifically I can't really see what happens in the last line where the remaining pointers are set into the block of memory (have i been told. And I am not sure how I will insert elements in the matrix/array when I am done allocating.
double ** malloc_array2d(size_t m, size_t n){
double **A;
size_t i;
A = malloc(m*sizeof(double *));
if (A == NULL) return NULL;
A[0]=(double *)malloc(m*n*sizeof(double));
if ( A[0] == NULL) {free(A); return NULL;}
for(i=1; i<m; i++) A[i]=A[0]+i*n;
return A;
}
Let's go Line by line:
A = malloc(m*sizeof(double *));
This line allocates space for m double pointers.
A[0] = (double *) malloc(m*n*sizeof(double));
A[0] is now a block of memory for m*n doubles, which is all the doubles we need for the 2d array.
for (int i = 1; i < m; i++) {A[i] = A[0] + i * n;}
because each A[i] is a block of n doubles, we want A[i] to start i*n doubles away from A[0].
Because all of this is in a solid block of memory, we can do some interesting things. For example, A[0][n] is the exact same double as A[1][0].
Furthermore, because everything is in one big block of memory, to access A[i][j] for any i < m, j < n, we just have to access the double at A[0] + i*j + j. This is much faster than going to A[i] which points to a double* B, and finding B[j].
Memory management is a difficult topic to understand and it takes some time. Hopefully this makes a bit more sense, and I hope I didn't confuse you even more :)
With this form of allocation, you start by allocating an array of pointers to other arrays, like so:
T **a = malloc( sizeof *a * N ); // N is the number of rows
sizeof *a is equivalent to sizeof (T *); each element of the array is going to be a pointer to T. When we're done, we have something like the following in memory:
+---+
a: | | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
...
+---+
| | a[N-1]
+---+
Now, for each of those elements, we allocate another chunk of memory to hold each element of type T:
a[i] = malloc( sizeof *a[i] * M ); // M is the number of columns
Each a[i] has type T *, so sizeof *a[i] is equivalent to sizeof (T).
After that's done, we have something that looks like this in memory:
+---+ +---------+---------+ +-----------+
a: | | a[0] ---> | a[0][0] | a[0][1] |...| a[0][M-1] |
+---+ +---------+---------+ +-----------+
| | a[1] ---> | a[1][0] | a[1][1] |...| a[1][M-1] |
+---+ +---------+---------+ +-----------+
| | a[2] ---> | a[2][0] | a[2][1] |...| a[2][M-1] |
+---+ +---------+---------+ +-----------+
...
+---+ +-----------+-----------+ +-------------+
| | a[N-1]--> | a[N-1][0] | a[N-1][1] |...| a[N-1][M-1] |
+---+ +-----------+-----------+ +-------------+
So basically what you've done here is allocate N separate M-element arrays of T, and then you collect the pointers to those arrays in an N-element array of T *.
You can access each element as a[i][j], like any normal 2D array; remember that the expression a[i] is defined as *(a + i); we offset i elements (not bytes!) from the address in a and then dereference the result. So a[i][j] is evaluated as *(*(a + i) + j ).
So, several things to remember with this form of allocation:
The "rows" of the array are not going to be contiguous in memory; the object in memory following a[i][M-1] is (most likely) not going to be a[i+1][0].
Since each "row" a[i] was allocated with a call to malloc, it must also be explicitly deallocated with a corresponding call to free before you deallocate a (always free in the reverse order that you malloc).
Even though we can treat a as a 2D-array, it does not have an array type, so you can't determine the size of the array using the sizeof a trick; you'll only get the size of the pointer type, not the total size of the array. So you'll want to keep track of the array size yourself.
You have to make each pointer of the pointer of poitners to point to valid malloc()ed data.
for (int i = 0 ; i < n ; ++i)
A[i] = (double *) malloc(m * sizeof(double));
You could also allocate it all at once, but then the notation A[i][j] will not work.

Pointer to pointer assigned to a two-dimensional array, points to wrong address

I'm new to C and although I thought I pretty much got the whole logic concerning pointers and arrays I've come across an issue that doesn't make any sense to me.
Consider a 2D array, say
double arr[][3] = { {1,2,3}, {4,5,6} };
and a pointer to a pointer
double ** ptrptr;
Now, let's say printing the address that arr and arr[0] point to, respectively, i.e.
printf( "%ld \n", (long) arr);
printf( "%ld \n", (long) *arr);
yields something like
140734902565640
140734902565640
since the first element of arr(array of arrays) and the first element of *arr(array of doubles) have the same location. So far so good. Now to my problem:
I do this:
ptrptr = (double **) arr;
printf( "%ld \n", (long) ptrptr);
printf( "%ld \n", (long) *ptrptr);
and I would expect the same output as before, but instead I get something like
140734902565640
4607182418800017408
So it seems that ptrptr and arr both evaluate to a pointer to the same location - as expected - but *ptrptr and *arr do not. Why?
Also, if I dereference once more, i.e. **ptrptr, I get a segmentation fault.
My logic went sth. like this, loosely speaking:
ptrptr == arr == &arr[0] should point to
*ptrptr == *arr == arr[0] == &arr[0][0] which in turn should point to
**ptrptr == *arr[0] == arr[0][0].
Although I found a workaround that will serve my purpose, I would still really like to understand why this doesn't work.
Let's talk about expressions and types.
Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" is converted ("decays") to an expression of type "pointer to T", and the value of the expression is the address of the first element of the array.
The expression arr has type "2-element array of 3-element array of double". In the line
printf( "%ld \n", (long) arr);
arr is not the operand of the & or sizeof operators, so it is converted to an expression of type "pointer to 3-element array of double", and its value is the address of the first element, or &arr[0].
In the line
printf( "%ld \n", (long) *arr);
since the expression arr has type "pointer to 3-element array of double", the expression *arr (which is equivalent to the expression arr[0]) has type "3-element array of double". Since this expression isn't the operand of the sizeof or unary & operators, it is converted to an expression of type "pointer to double", and its value is the address of the first element, or &arr[0][0].
In C, the address of the array is the same as the address of the first element of the array (no separate storage is set aside for a pointer to the first element; it is computed from the array expression itself). The array is laid out in memory as
+---+
arr: | 1 | 0x0x7fffe59a6ae0
+---+
| 2 | 0x0x7fffe59a6ae8
+---+
| 3 | 0x0x7fffe59a6aec
+---+
| 4 | 0x0x7fffe59a6af0
+---+
| 5 | 0x0x7fffe59a6af8
+---+
| 6 | 0x0x7fffe59a6afc
+---+
So the following expressions will all yield the same value, but the types will be different:
Expression Type Decays to
---------- ---- ---------
arr double [2][3] double (*)[3]
&arr double (*)[2][3] n/a
*arr double [3] double *
arr[i] double [3] double *
&arr[i] double (*)[3] n/a
*arr[i] and arr[i][j] both yield a double value.
So now let's look at ptrptr:
double **ptrptr = (double **) arr;
printf( "%ld \n", (long) ptrptr);
printf( "%ld \n", (long) *ptrptr);
The first thing that we notice is that ptrptr is not an array expression, so the conversion rule above doesn't apply. We assign it to point to the first element of arr, but after that it behaves like any other pointer, so the expressions ptrptr and *ptrptr will have different values. Since ptrptr points to the first element of the array (&arr[0][0]), *ptrptr yields the value stored at the first element, which is 1.00. Just so happens that when you interpret the bit pattern for 1.00 as a long integer on this particular platform, it comes out as 4607182418800017408.
Here's some code that may make the above a little more clear:
#include <stdio.h>
int main( void )
{
double arr[][3] = {{1,2,3},{4,5,6}};
double **ptrptr = (double **) arr;
printf( " arr: %p\n", (void *) arr );
printf( " &arr: %p\n", (void *) &arr );
printf( " *arr: %p\n", (void *) *arr );
printf( " arr[0]: %p\n", (void *) arr[0] );
printf( "&arr[0]: %p\n", (void *) &arr[0] );
printf( " ptrptr: %p\n", (void *) ptrptr );
printf( "*ptrptr: %p (%f %ld)\n", (void *) *ptrptr,
*(double *) ptrptr, *(long int *) ptrptr );
return 0;
}
And here's the output:
arr: 0x7fffe59a6ae0
&arr: 0x7fffe59a6ae0
*arr: 0x7fffe59a6ae0
arr[0]: 0x7fffe59a6ae0
&arr[0]: 0x7fffe59a6ae0
ptrptr: 0x7fffe59a6ae0
*ptrptr: 0x3ff0000000000000 (1.000000 4607182418800017408)
Again, *ptrptr gives us the value at the first element of the array, which is 1.0, but we're interpreting that bit pattern as a pointer value (0x3ff0000000000000) and a long integer (4607182418800017408).
The difference is most clearly illustrated with a diagram.
This is a normal 2D array in C, double[4][3].
+---+---+---+
| | | |
+---+---+---+
| | | |
+---+---+---+
| | | |
+---+---+---+
| | | |
+---+---+---+
This is a double pointer in C, double **. It is pointing to the head of a double *[4], each of which points to the head of a double[3].
+----------+ +---+ +---+---+---+
| +---->| +------>| | | |
+----------+ +---+ +---+---+---+
| +-----+
+---+ | +---+---+---+
| +---+ +>| | | |
+---+ | +---+---+---+
| +-+ |
+---+ | | +---+---+---+
| +-->| | | |
| +---+---+---+
|
| +---+---+---+
+---->| | | |
+---+---+---+
Notice that the syntax for accessing elements is the same.
double arr[4][3];
arr[1][2] = 6.28;
double **ptr = ...;
ptr[1][2] = 6.28;
However, what is actually happening is quite different. So you cannot simply cast one type to the other.
With the double[4][3], you find an element by doing a little bit of math to calculate the offset.
With the double **, you find an element by following a chain of pointers.
An array can be seen as a "label":
Unlike a pointer, it doesn't have an lvalue, and you cannot set it to a different value after declaring it.
Try arr = ptrptr or arr = (double**)0x80000000, and you'll get a compilation error.
That is why, for any type of array arr, the following is always true: arr == &arr.
In addition, please note the memory-map differences:
double arr[3][3]; // A consecutive piece of 9 `double` units.
double** ptrptr // 1 pointer unit.
ptrptr = new double*[3]; // A consecutive piece of 3 pointer units.
for (int i=0; i<3; i++)
ptrptr[i] = new double[3]; // 3 separate pieces of 3 consecutive `double` units.
So not only does ptrptr consume an additional amount of 4 pointer units in comparison with arr, but it also has the 9 double units allocated in 3 separate sections in memory.
Hence arr = &arr = &arr[0] = &arr[0][0], but ptrptr != *ptrptr != **ptrptr.

Resources