Whats the value of &y[0] == &x.a? - c++

Got this piece of code from an homework assignment. I should figure out, what the value of &y[0] == &x.a. Btw sorry for not formatting properly, I tried my best.
So my approach was; y[0] = 1, and &y[0] means the address of 1, which is 0 in the array?
x.a confused me the most. Does it mean element a of the struct x? So x.a == 1 ? And its address would also be 0? So the boolean would come out as true(1), because 0 == 0. But the solution says it's false, but why?
struct my_struct {
int a;
double b;
int c;
};
my_struct x = { 1, 2, 3 };
int y[] = { 1, 2, 3 };
I expected the output 1 but apparently it's 0, but why?

&y[0] means the address of 1, which is 0 in the array
Almost, but not quite. The addresses given to you by the address-of operator are absolute values, not ones relative to the "container". So &y[0] is effectively the same as y, and &x.a is effectively the same as &x.
y is a value that can be converted to a pointer to the first element. This must and will be distinct (numerically) from &x which occupies a different part of memory.
| x.a | <- &x points here
| x.b |
| x.c |
| y[0]| <- y points here
| y[1]|
[ y[2]|
All of the above skims over the fact that those pointers might have different types. This matters to the compiler when checking the correctness of your code, but is ultimately discarded afterwards, as all addresses effectively become numerical values.

This expression
&y[0] == &x.a
compares addresses (pointers) of two different objects. The first one belongs to the array y (that is y[0] is the first element of the array) while the second one belongs to an object of the type my_struct (a is a data member of the object x).
The objects occupy different extents of memory.
So the expression yields false that is the addresses of the different objects are not equal each other.
I can provide an example when comparing two different pointers (that is pointers of different types) yields true.
Consider the following example
struct my_struct {
int a;
double b;
int c;
};
my_struct x = { 1, 2, 3 };
then ( void * )&x == ( void * )&x.a evaluates to 1.
Note: the casting is required because the operands have different types and there is no implicit conversion from one type to another.
One more example
#include <iostream>
#include <iomanip>
int main()
{
int y[] = { 1, 2, 3 };
std::cout << std::boolalpha
<< ( ( ( void * )&y == ( void * )y ) && ( ( void * )&y == ( void * )&y[0] ) && ( y == &y[0] ) )
<<'\n';
}
The program output is
true
All three expressions &y, y, and &y[0] have the same value (address). (An array used in an expression with rare exceptions is converted to pointer to its first element.)

The address of two separate declared variables are almost never equal (==).
Here, my_struct x = { 1, 2, 3 }; is declared separately from int y[] = { 1, 2, 3 };. When they're declared, they're given their own space in memory to occupy, so they're in two different spaces in memory. Even though the values stored by struct my_struct x and array int y[] are the same, they're stored in their own memory spaces.
If you want to see the actual memory locations, you can do this with a simple cout print statement:
cout << "&y[0]: " << &y[0] << endl;
cout << "&x.a: " << &x.a << endl;

&y[0] and &x.a are different addresses. If you are trying to check if they are the same, you can write a simple programm. And you will see the result.
struct my_struct {
int a;
double b;
int c;
};
int main()
{
my_struct x = { 1, 2, 3 };
int y[] = { 1, 2, 3 };
std::cout << "&y[0] = " << &y[0] << "\n&x.a = " << &x.a << "\n&y[0]==&x.a = " << (&y[0]==&x.a);
}
The output is:
&y[0] = 0x72261501a440
&x.a = 0x72261501a450
&y[0]==&x.a = 0
So we can see different values of &y[0] and &x.a. They are not the same. and also &y[0]==&x.a = 0 and it means false

The & will return memory address (depending on your target platform that is usually 16 to 64 bit number, either representing physical memory address, or virtual logical address mapped to physical memory by OS (on modern OS like linux or MacOS, the user process doesn't see true physical addresses by default)).
So this part of source:
my_struct x = { 1, 2, 3 };
int y[] = { 1, 2, 3 };
will instantiate both the structure and array in the memory as global variables, i.e. they will have memory addresses like 0x12345678, etc... and they will not overlap, if size of my_struct is for example 20 bytes, and x address is 0x1000, the array y will be in memory somewhere beyond it (address of y is also address of first element, in C/C++ the arrays are zero-cost abstraction, i.e. where the array starts, there's the first element already stored, and the rest of elements follow on consecutive memory addresses).
So your original expression &y[0] == &x.a is comparing one (absolute) address of particular element of the array with (absolute) address of structure member, and because they are instantiated in different regions of memory, the addresses will surely differ (as long as the index into array is within bounds) = false. You don't even need to check details which addresses precisely are compared.
But for completeness, the y[0] is the first element of array y, and address of that is identical to y itself. If there would be &y[1], that's address of second array element, with type int that means the &y[1] is equal to y+1 and/or to ((void*)y) + sizeof(int) ... in CPU machine code terms, that means if array starts at memory at address 0x2000, and int is 64 bits on your target platform, the second element starts at address 0x2008 (+8 bytes (64 bits) away from the start of array).
The &x.a is similarly full address where the actual value of member a resides in memory, so if the structure instance x starts at address 0x1000, the address of x.a will be also 0x1000 in this case (again zero cost of abstraction and it's simple struct type, if it would be class-like, having virtual functions, the member a would be probably a bit offset after the implicit virtual table pointer which is usually put at beginning of class/struct instance ... but this is implementation/platform specific, i.e. depends how particular compiler implements virtual functions/calls). For example the address of &x.b would be then 0x1008 (again I assume 64 bit int type, and I assume there's no extra padding applied in the my_struct, with different assumptions/conditions the offset of b from start of structure may be different than +8).

& gives you the address of an object.
&y gives you the address of the array y.
&y[0] gives you the address of the first element in array y which is equal to &y.
&x gives you the address of the struct x
&x.a gives you the address of the first member of the struct x which is equal to &x.
Since x and y are at different locations the addresses differ.

Related

Order of Evaluation for passing function arguments - Order of operations for F1( int F2( int& x ), int x )

Okay so I was writing some code earlier. This was the line specifically:
EnterNode( FindNode( terrain_X, terrain_Y, travel_dir ), travel_dir );
I noticed after testing my program something weird was happening. The value being received by the outer function was not the value I was reading when I inspected the stack.
I made an example program:
https://ideone.com/wNjJrE
#include <iostream>
int modifyRef(int& A)
{
A = 0;
std::cout << "\nint& A = 0";
return A;
}
void TakeValues(int X, int Y)
{
std::cout << "\nX = " << X;
std::cout << "\nY = " << Y;
}
int main()
{
int Q = 9;
TakeValues(modifyRef(Q), Q);
std::cout << std::endl;
system("pause");
return 0;
}
This is the output I receive:
int& A = 0
X = 0
Y = 9
I would expect Y to also be 0. How is the order of operations defined for parameter binding to function calls?
(My apologies if I am not using the correct terminology.)
The evaluation order of function arguments is unspecified. When you write:
TakeValues(modifyRef(Q), Q);
you are relying upon the fact that modifyRef(Q) to be evaluated before Q. But the evaluation order of function arguments is unspecified - it is not necessarily the case that modifyRef(Q) will be sequenced before Q nor vice versa.
In this case, Q (the second argument) gets evaluated first. So we read 9, and initialize the parameter Y with it. Then we evaluate modifyRef(Q), which zeros out Q and returns it, which leads to initializing X with 0.
Try changing the signature of your functions:
int ModifyRef( int& A );
void TakeValues( int X, int Y );
to the following...
int& ModifyRef( int& A );
void TakeValues( int& X, int& Y );
and see what your output will be in main when you call this line of code:
int q = 9;
TakeValues( ModifyRef(q), q );
then reverse the order of the parameters as such
TakeValues( q, ModifyRef(q) );
and compare the results.
When I did this on my machine Win 7 64bit with VS2015 Community on an Intel Core2 Quad Extreme the results I was given for both situations when using references were the same and the output I am getting is:
I changed the value of A in ModifyRef() from 0 to 7 for testing purposes.
int& A = 7
X = 7
Y = 7
for both situations where either the inner function is the first parameter and the stand alone variable is the second or the inner function is the second parameter with the stand alone variable as the first.
Since I am receiving the same results it appears to me that do to how the compiler creates and handles the stack pointer the ModifyRef() appears to be evaluated first and once that function is finished since q is now being referenced instead of being a stack copy it is being overwritten by whatever value it is being set to within the ModifyRef function.
I also modified your ModifyRef() function slightly so that I can see what its passed in parameter is instead of having a number hard coded into its print out statement.
int& ModifyRef( int& A ) {
A = 7; // Changed Value From 0 To 7 For Testing Purposes
std::cout << "n\int& A = " << A;
return A;
}
However this effect may only apply when using only references.
When I revert back to your original function signatures and I call it in this order:
q = 9;
TakeValues( ModifyRef( q ), q );
As is your original code was presented the output I am getting is:
int& A = 7
X = 7
Y = 9
However when I reverse the parameters to:
q = 9;
TakeValues( q, ModifyRef( q ) );
My output is:
int& A = 7
X = 7
Y = 7
So what I am seeing in these two situations is slightly different.
In the first order of parameters, Y or the second parameter is being set to 9 as q is initialized to 9 and Y within TakeValues() is printing out a stack copy with a value of 9. Then X is being valuated by ModifyRef() where q has a value of 9 but then it is being modified since it is a reference so when TakeValues() sets X from q, q was already changed from 9 to 7 so X is now being set to 7.
In the second order of parameters it appears that ModifyRef() is being called first and changes q from 9 to 7 so TakeValues() is setting Y to 7 and since this function is using a reference q was also changed from 9 to 7, so when the first parameter is taking q to set X q was already changed from 9 to 7.
I do not know if this is compiler dependent but at least on my machine it appears that the call stack for the parameters is happening from farthest right to left. This also makes sense when you think about it due to when functions have default values.
Example:
class foo {
void bar( int a, int b, int c = 3 ) {
std::cout << a << ", " << b << ", " << c << std::endl;
}
};
All default values in a function declaration must be to the far right for you can not have:
class foo {
void bar( int a = 1, int b, int c ) { ... } // Compile Error
};
I hope this helps to clarify what is going on within your code when you begin to call a function within a function as a parameter either if you are using passing by value(stack copy) or by reference.

pointer to a variable is not updating variable when dereferenced

This is a debugging problem I've been trying to solve. I know the bit mask I need to apply to make b equal a. I inspected with gdb to find the difference between a and b. The variables b and a are char[] types and set prior to reaching the 'bug'.
#include <string.h>
int main() {
char a[1] = "a";
char b[1] = "b";
int *x;
x = (int *) b;
// bug in next line
*x = *x & 0xffffffff;
return memcmp(a, b, 1);
}
Until a equals b, I can't solve the problem. The only constraint given is that the bug is in the line noted, no other code is to be changed. There is no rule saying I can't add lines after the bug, though and before the memcmp().The issue I find is that nothing I do to the bit mask changes the value of b, ever. I've set breakpoints and inspected the value of x and *x before and after the bug, but x seems to not change.
Breakpoint 1, main () at test.c:9
9 *x = *x & 0xffffffff;
(gdb) print (int) a
$1 = -6922
(gdb) print (int) b
$2 = -6921
(gdb) print (int) x
$3 = -6921
(gdb) step
Breakpoint 2, main () at test.c:10
10 return memcmp(a, b, 1);
(gdb) print (int) a
$4 = -6922
(gdb) print (int) b
$5 = -6921
(gdb) print (int) x
$6 = -6921
I don't see how this can be solved the way requested, by modifying the constant in the line where the bug is. Any help to understand how to use x to update b using a bitwise mask would be appreciated.
x is a pointer; casting it to an int simply gives you the address as a decimal number.
a and b are both arrays, which will decay to a pointer when you do operations that require a pointer. By casting them to int you're again getting the address of the variable. The address doesn't change with the operation you're performing, even when the contents at that address changes.
Since a and b are both smaller than an int, your code is likely to mess up in ways that are extremely painful. Even if they were the right size, this isn't guaranteed to do the right thing.
You are trying to change the address of b but in
*x = *x & 0xffffffff;
You are changing the value because you are dereferencing x. Yyou need to apply the manipulation to x itself like
x = x & 0xffffffff;
And then you need to reassign x into b.
This will run afoul of the strict aliasing rules.

Accessing the value pointed by a pointer

Edit: This question should be considered abandoned. I have flagged this question for deletion as i do not know how to even proceed any more at this point. I thank all of you for your willingness and taking the time out of your day to help me.
I am reading and following the documentation at cplusplus on data structures. I have been trying to figure out why the compiler will not accept " *pfruit.weight; " on my own for hours. It am sure it is something simple that i am missing.
error C2228: left of '.weight' must have class/struct/union
1> type is 'product *'
1> did you intend to use '->' instead?
"The operand to the left of the period (.) is not a class, structure, or union."
So how do i correctly access the value pointed by a pointer member?(Not access a reference)
#include <iostream>
using namespace std;
struct product
{
int weight;
float price;
} ;
int main ()
{
product afruit;
product * pfruit;
pfruit = &afruit;
pfruit->weight;
//Access a member of an object to which we have a reference
//equivalent to: (*pfruit).weight
*pfruit.weight; //<------ I am so sorry, i meant This is the problem, i am using a reworded line of code from an example and it isn't compiling and returning the error.
//Access the value pointed by a pointer member called weight;
//equivalent to: *(pfruit.weight)
system("pause");
return 0;
}
This code
struct product
{
int weight;
float price;
} ;
int main ()
{
product * pfruit;
*pfruit.weight;
is an operator precedence error. The rules of C++ are that . has higher precedence than *. *pfruit.weight would be correct if pfruit was a struct and weight was a pointer, but in your code it's the other way around, pfruit is the pointer and weight is just an int. So you need to add brackets to apply the operators the right way around
(*pfruit).weight;
You may use the following statement :
(*pfruit).weight;
Instead of :
*pfruit.weight;
which evaluates at compile-time to the following because of operator precedence :
*(pfruit.weight)
This is wrong because you are dereferencing a non-pointer value and trying
to dereference a pointer with the . operator instead of ->.
EDIT :
Here's some piece of response and informations about pointers and to your question as I understood it.
#include <iostream>
using namespace std;
struct product
{
int *weight;
float price;
};
int main()
{
product afruit;
product *pfruit;
pfruit = &afruit;
// Allocating 10 * sizeof(int) bytes of memory. ptr will now point to the first sizeof(int) bytes
// of the memory pointed by the weight pointer.
pfruit->weight = new int[10];
// The first sizeof(int) bytes of the memory pointed by ptr (equivalent to *ptr) equals now the value 4.
pfruit->weight[0] = 4;
// The second sizeof(int) bytes of memory pointed by ptr (or (*ptr + 1)) equals now 42.
pfruit->weight[1] = 42;
// Dereferencing pfruit AND the pointer to the weight, thus returning the VALUE of the first sizeof(int) bytes of weight (4)
// instead of the value of the pointer to weight (0x8b4e008 for instance).
int value = *(pfruit->weight);
std::cout << "Value of pfruit->weight[0] = " << pfruit->weight[0] << std::endl
<< "Value of pfruit->weight[1] = " << pfruit->weight[1] << std::endl
<< "Value of *(pfruit->weight) = " << *(pfruit->weight) << std::endl
<< "Value of *(pfruit->weight + 1) = " << *(pfruit->weight + 1) << std::endl
<< "Value of ptr = " << std::hex << pfruit->weight << std::endl;
// Prints :
// Value of pfruit->weight[0] = 4
// Value of pfruit->weight[1] = 42
// Value of *(pfruit->weight) = 4
// Value of *(pfruit->weight + 1) = 42
// Value of ptr = 0x8b4e008
//
// Note that ptr[0] == *ptr and ptr[1] == (*ptr + 1)
return (0);
}
once try this u may get it this can be written as pfruit->weight or (*pfruit).weight
(*pfruit).weight;
product afruit;
product * pfruit;
pfruit = &afruit;
pfruit->weight;
pfruit is a pointer and has the address of the struct afruit.
We use pfruit->weight when we access struct members using a pointer .
When you use a struct name you use . operator.
Now, pfruit is the pointer . Therefore, the struct itself is *pfruit
*pfruit.weight;
This is interpreted as *(pfruit.weight), because .has higher precedence than *. and you can't use . operator with a pointer on LHS
You have to clearly show that you mean (*pfruit).weight
Bit late to the party, but try this:
It sounds like you are expecting values like 'int weight' to be stored as pointers to data somewhere else in the same way that the product is being referred to via pfruit. Some languages do do this, but in C++ simple data members like your weight and price are going to be stored actually in the product data structure. So, if afruit happens to exist at address 1000 in memory, then pfruit will have the value 1000:
... 1000 1001 1002 1003 ...
--+-----+-----+-----+-----+--
| afruit... | | |
--+-----+-----+-----+-----+--
^
|
pfruit = 1000
This means that your weight (int) and price (float) data members will be stored here, one after the other. (and since an int and float are typically 32 bits in size, i.e. 4 bytes, the memory looks like this:
... 1000 1001 1002 1003 1004 1005 1006 1007 ...
--+-----+-----+-----+-----+-----+-----+-----+-----+--
| afruit... |
| <------ weight -----> | <------ price ------> |
--+-----+-----+-----+-----+-----+-----+-----+-----+--
^
|
pfruit = 1000
Consequently, pfruit also happens to point at the weight data (because it's first in the structure), but the price data is 4 bytes further along.
So, when you say you want to get to the data 'pointed to by weight' it doesn't make sense because it actually turns out that you don't need to because when you access the weight member you are already referring to the weight value. It's right there, in the memory occupied by afruit.
If you ARE trying to get a pointer TO the weight (or cost) data itself (instead of afruit, or actually from pfruit) you can do something like this:
int* pweight = &afruit.weight; //from an actual product object
float* pcost = &pfruit->cost; //via a pointer to a product object
There are now various ways to access the members of the product structure afruit:
afruit.weight = 20; //directly access the structure member
pfruit->weight = 10; //follow the structure pointer
*pweight = 15; //derefernce the int pointer
I hope this helps someone, at least a little bit.
Sam

Incrementing pointers in C++

Why are the two following code segments not equivalent?
void print (char* s) {
if (*s == '\0')
return;
print(s+1);
cout << *s;
}
void print (char* s) {
if (*s == '\0')
return;
print(++s);
cout << *s;
}
The ++ operator increments the pointer value, but then returns the original value ... so print(s++) will print the value of s before the increment, since even though it adds a value of 1 to s, making the value stored at s equal to s+1, it still returns the original value of s as the result of the operation. On the otherhand print(s+1) prints the value after the increment, but very importantly does not modify the original value of s. So the result of the statement s+1 is just a new temporary pointer value ... the original value of s is not modified.
Furthermore, since you've incremented and changed the value of s with the ++ operator, when you call cout, you're now printing the value to wherever the new pointer is pointing (this could cause a crash or segmentation fault if you're not careful and there's no user accessible memory at the new memory location s is pointing to). With s+1, the value of s remains unmodified, so the result of cout will be to wherever s was originally pointing.
Edit:
As Michael points out, this is actually a recursive function, so the second example simply keeps calling print() with the same argument, since as mentioned before, the returned value from s++ is the original value of s. That means you'll end up with a stack overflow at some point and just crash unless the value that s pointed to was already the NULL character.
Since it looks like the OP changed print(s++) to print(++s), which is hugely different, here's an explanation for this new version.
In the first example, you have:
print(s+1);
cout << *s;
s+1 does not modify s. So if s is 4, and you print(s+1), afterwards s will still be 4.
print(++s);
cout << *s;
In this case, ++s modifies the local value of s. It increments it by 1. So if it was 4 before print(++s), it will be 5 afterwards.
In both cases, a value equivalent to s+1 would be passed to the print function, causing it to print the next character.
So the difference between the 2 functions is that the first one will recursively print character #0, then 1, 2, 3, ..., while the second function prints 1, 2, 3, 4, ... (it skips the first character and prints the "\0" afterwards).
Example:
For the s+1 version, print("hello") will result in h e l l o
For the ++s version, print("hello") will result in e l l o \0
Both of the expressions s++ and s+1 are to do with increasing the position of the pointer itself, not the value contained at the pointer locations
The value of s++ is just s, and the value of s+1 is, well, one position further on than s!
The value of s after executing s++ is one position further on than it was before. After using s+1, the value of s is unchanged.
Therefore the order they print out the letters is reversed!
I will try to explain an example of pre and post increment from which you can solve the question posted yourself.
#include <iostream>
void foo(int num)
{
std::cout << num << "\n" ;
}
int main()
{
int number = 10 ;
foo( number++ ) ;
foo( ++number ) ;
foo( number + 1 ) ;
getchar() ;
return 0 ;
}
Output:
10
12
13
Why 10?
foo( number++ ) ;
Post-increment operation is done on number. Meaning, value of number is first passed to foo and then the value of number is incremented upon foo return. So, after function return, number is 11.
Why 12?
foo( ++number ) ;
Pre-increment operation is done on number. Meaning, before even call to foo, the value of number is incremented to 12. And then it is passed to foo. So, even after the function return, number is still 12.
Why 13?
It's just straight forward. Value of number is not modified but passed a value adding 1 to the value of number. In this process, number is not modified. So, even after function return, number is still 12.
Hope this helps to solve the problem yourself ( though in your case it is paper-pencil exercise ) :)

Pointer Arithmetic? What is the difference between these three expressions?

So I am trying to understand two things:
What are the differences between these three expressions?
What is the equivalent to the first expression (A)?
Here is the code (fixed):
#include "stdafx.h"
void someFunc(double** pArray, int length)
{
for(int i = 0; i < length; i++)
{
//this works perfectly
double expressionA = *(*pArray + i);
//this crashes : Unhandled exception at 0x00da13ff in pptrtest.exe: 0xC0000005: Access violation reading location 0xcccccccc.
double expressionB = **(pArray + i);
//this crashes : Unhandled exception at 0x00da13ff in pptrtest.exe: 0xC0000005: Access violation reading location 0xcccccccc.
double expressionC = *pArray[i];
}
}
int main()
{
double arr[] = { 1, 2, 3, 4, 5 };
double* pArr = arr;
someFunc(&pArr, sizeof arr / sizeof arr[0]);
return 0;
}
I think the problem is, that [] is applied before *, so in expr C you are doing
*(ppDoubleArray[i])
actually the equivalent of expr B, but the expr A does
(*ppDoubleArray)[i]
(assuming the 1 in the expression should be an i, otherwise thats different anyway).
You have a pointer to a pointer of double. Think of it as a pointer to an array of doubles. Either ways, the following is their meanings:
double expressionA = *(*ppDoubleArray + 1); is the same as: double expressionA = *((*ppDoubleArray) + 1);
double expressionB = **(ppDoubleArray + 1); is the same as: double expressionB = *(*(ppDoubleArray + 1)); which means you want to de-reference ppDoubleArray[1] which am guessing doesn't exist.
double expressionC = *ppDoubleArray[i]; is the same as: double expressionC = *(ppDoubleArray[i]); -- again, you are de-referencing ppDoubleArray[i] which does not exist.
Like everyone else said, you need to be mindful of the order of preference. Basically, dereference first then index your array.
double expressionB = **(ppDoubleArray + 1);
Lets look at memory for this guy, on the stack you've got a pointer to a pointer to a double. So if its an address in memory for a 32-bit processor, the stack could look something like:
stack: |ppDoubleArray ptr |local vars, other args, or garbage |more locals/etc
addr: |0 bytes from stack |4 bytes from stack |8bytes...
So when we look at the first part of the expression
(ppDoubleArray + 1)
This says "go one pointer past ppDoubleArray". Go to the next pointer by going to the next spot in memory. What is the next spot in memory past ppDoubleArray? Look at the stack above, its probably some local variables or the other argument. So now you have who knows what (maybe the contents of length? one of the doubles? garbage?) and you're going to treat it as if it were a valid address somewhere. You then follow that supposed pointer, by dereferencing:
**(ppDoubleArray + 1)
And crash!
So, for example, if length was 5, ppDoubleArray+1 might grab 5 and dereference "5" looking for something at that address.
This code:
double expressionC = *ppDoubleArray[i];
works the same way, the [] take precedence over the dereference. So you go to the ith memory location past ppDoubleArray and assume its pointing at your array.
Rearranging the order of operations with parent would, however work:
(*ppDoubleArray)[i]
Assuming ppDoubleArray is a pointer to an array of doubles:
The second one crashes because you are adding one to the pointer to the array not selecting the correct index from it.
The third one crashes because of the precedence of the operators. you are selecting the ith element which is not the array (unless i is 0) and then dereferencing that.
The important thing to remember here is how to deal with a pointer to an array (or a pointer to a pointer) You dereference it once to get to the array, at which point you can do the arithmetic and then dereference it again to get to the element.
aSCII art Time:
---> = derefering
double expressionA = *(*ppDoubleArray + 1);
ppDoubleArray ---> .
+1
. ---> expressionA
double expressionB = **(ppDoubleArray + 1);
ppDoubleArray
+1
. ---> . ---> expressionB
double expressionC = *ppDoubleArray[i];
ppDoubleArray
+i
. ---> . ---> expressionC