What is the significance of a pointer to a pointer? - c++

What is the difference between doing this:
int i = 5, j = 6, k = 7;
int *ip1 = &i, *ip2 = &j;
int *ipp = ip1;
and doing this:
int **ipp2 = &ip1;
Don't they do the same thing? hold a pointer(ip1) which points to a variable, i?

ipp2 points to ip1. This is entirely different from pointing to i.
Sample code:
int *ip1 = &i;
int **ipp2 = &ip1;
printf("%d\n", **ipp2); // 5
ip1 = &j;
printf("%d\n", **ipp2); // 6

All the variables have a location in memory where their values are held. Let's explore the relationships between the values of i, ip1, ipp, and ipp2
This is what you get when the statement i = 5; is executed. i has its location in memory and the value at location is set to 5.
i -> +--------+
| 5 |
+--------+
^
|
A1 (address of i)
This is what you get when the statement int* ip1 = &i; is executed. ip1 has its location in memory and the value at that location is set to the address of i, which we designate as A1.
ip1 -> +--------+
| A1 |
+--------+
^
|
A2 (address of ip1)
This is what happens when you execute the statement int* ipp = ip1;. The value at the memory location of ipp is set to the value of ip1, which is A1.
ipp -> +--------+
| A1 |
+--------+
^
|
A3 (address of ipp)
This is what happens when you execute the statement int** ipp2 = &ipp;. The value at the memory location of ipp2 is set to A3, which is the address of `ip1.
ipp2 -> +--------+
| A3 |
+--------+
^
|
A4 (address of ipp2)
How does dereferencing work:
*ip1 = *A1 = 5
*ipp = *A1 = 5
*ipp2 = *A3 = A1
**ipp2 = **A3 = *A1 = 5

Pointers are often used to change the value of a variable inside a function:
void incr(int *ip) { *ip++; }
void f() { int i = 0; incr(&i); printf("%d\n", i); // 1
Now it's not any different with a pointer to a pointer. You can pass the pointer to a pointer to a function, and that function can change what that pointer points to: the original pointer!
char *mom = "mom";
char *pop = "pop";
chooseMomOrPop(int choosePop, char **momOrPop) { *momOrPop = choosePop ? pop : mom; }
void f() { char *mp = mom; chooseMomOrPop(1, &mom); printf("%s\n", mom); } // pop

Very simple: a pointer is an address where a variable stays in memory. Since a pointer is itself a variable, its address could be stored in another pointer, and so on. To better have in mind how a pointer is and how it works, just think it is an address. The type of the pointer, ie int in int * refers to the type of the data pointed, and effect how the pointer "react" to addition or subtraction, as described in pointer arithmetic. A 'pointer to pointer of int' is an int**, so is always an address pointing to an int*, and when incremented it will move the address to as many byte as necessary to point the next int*

Related

What happens after we assign to a dereferenced pointer an another dereferenced pointer?

I can't understand what happens after line *p1=10, what happens with *p2 variable, and in the end how does secondvalue variable gets 20.
#include <iostream>
using namespace std;
int main ()
{
int firstvalue = 5, secondvalue = 15;
int * p1, * p2;
p1 = &firstvalue;
p2 = &secondvalue;
*p1 = 10;
*p2 = *p1;
p1 = p2;
*p1 = 20;
cout << "firstvalue is " << firstvalue << '\n';
cout << "secondvalue is " << secondvalue << '\n';
return 0;
}
Output of command
firstvalue is 10
secondvalue is 20
I can't understand how the secondvalue variable gets a value of 20.
If we assigned *p2=*p1 mustn't secondvalue variable get 10?
Lets see this step by step:
int firstvalue = 5, secondvalue = 15;
This initializes two 4 byte integers.
---------------------------------------
| | |
| firstvalue (5) | secondvalue (15) |
| | |
---------------------------------------
Initializes int pointers and sets their values
int * p1, * p2;
p1 = &firstvalue;
p2 = &secondvalue;
p1 now stores the address of firstvalue, and p2 stores the address of secondvalue
---------------------------------------
| (*p1) | (*p2) |
| firstvalue (5) | secondvalue (15) |
| | |
---------------------------------------
p1 p2
Now you assign 10 to *p1. What this does is that it modifies the value at the memory address which was stored in p1:
*p1 = 10;
---------------------------------------
| (*p1) | (*p2) |
| firstvalue (10) | secondvalue (15) |
| ^^ | |
---------------------------------------
p1 p2
Now you assign *p1 to the memory address stored in p2. *p1 dereferences the pointer p1 and returns you the value it is pointing to.
*p2 = *p1;
---------------------------------------
| (*p1) | (*p2) |
| firstvalue (10) | secondvalue (10) |
| | ^^ |
---------------------------------------
p1 p2
Now you change the memory address p1 is pointing to. Now p1 and p2 points to the same variable
p1 = p2;
---------------------------------------
| | (*p1) (*p2) |
| firstvalue (10) | secondvalue (10) |
| | |
---------------------------------------
p1 p2
Finally, you modify the value at the memory address stored by p1, (that is also p2).
*p1 = 20;
---------------------------------------
| | (*p1) (*p2) |
| firstvalue (10) | secondvalue (20) |
| | ^^ |
---------------------------------------
p1 p2
p1 = p2;
This doesn't copy the value pointers point to, just "address" the p2 points to. Thus at this point both p1 and p2 point to the secondvalue, and changing either of them applies changes to the secondvalue
If we assigned *p2=*p1 mustn't secondvalue variable get 10?
It sure does. And if that was the last assignment, then that what would happen. But perhaps you've noticed that there are more assignments, that follow this one:
p1 = p2;
p1 is now pointing to the same value that p2 is pointing to. p2 is pointing to secondvalue, so p1 is now pointing to secondvalue, too.
*p1 = 20;
The value that p1 points to gets assigned 20. And that's how secondvalue gets assigned 20.
Yeah, but when you p1 = p2; you make so p1 now stores the reference to second value, and in the line below you assing *p1 (this is secondvalue) to 20.
So in your code flow firstvalue is assigned to 10 on *p1 = 10; and secondvalue is assigned to 20 on *p1 = 20;
So, pointers and references (and de-referencing, oh my!)
Alas, this is where vocabulary makes a difference, and people love this particular selection of vocabulary. So here it is, explained in all its glory.
Values
A value is something that exists somewhere in memory (like a variable, though not necessarily). To be useful, we programmers like to give values both a type and a name.
int x = 74;
double pi = 3.141592;
string name = "Hannah";
References
A direct reference is an alias, another name, if you will, for a value.
int x = 7; // type:int, name:x, value:7
int& z = x; // z = another name for x
z = 12; // x = 12;
Using aliases is something we do in normal life.
I call my father “Dad”.
Dad’s friends call him “Bill”.
My friends call him “Mr. Greer”.
Different names. Same person.
Now, some C++ purists will tell you these are just plain references. Well, yes... and no. Yes, as far as the C++ Language is concerned. No, as far as understanding things is concerned. Just remember that when talking to C++ people, the word “reference”, unless otherwise qualified with some adjective, always means direct reference, or alias.
Pointers
Before C++ introduced “references” as part of the language abstraction, we had indirect references — also called pointers.
It is indirect because a pointer is not itself a reference. But you can get a reference from a pointer’s value by dereferencing it using the * operator.
The term is a bit wonky (because it looks like you are “un”-referencing it), but the opposite is true: you are taking that indirect reference (the pointer value) and effectively converting it into an (unnamed) direct reference, an alias for the “pointed-to” (or “referenced”) value.
int x = 7;
int* px = &x; // type=(pointer to int) name=“px” value=(address of x)
*px = 12; // (direct reference to x) = 12
In other words px is a pointer with an indirect reference to — i.e. address of — x.
Thus when we dereference px with the * operator, we transform the expression into a direct reference to x — at which point we have access to read or modify x’s value just as if we had x in hand to begin with.
I suspect the term “dereference” came about because we are “un”-indirect referencing a pointer value to obtain a direct reference, but all the people who created the term are dead now and I don’t know of any place they felt the need to explain it.
WHAT YOU MEANT TO ASK
So, a dereferenced pointer is an alias for the target object. You want to know what happens if you don’t dereference the pointers, which would be:
“What happens if we assign an un-dereferenced pointer to another un-dereferenced pointer?”
... and un-dereferenced means not dereferenced, so you are basically asking:
What happens when we assign one pointer to another?
The answer at this point should not be a surprise: the same thing that happens whenever we assign any variable’s value to another.
int x = 7;
int* p1 = &x; // p1 now “points to” x
int* p2 = p1; // and now p2 also “points to” x
Remember, the value of a pointer is the address of another object in memory, and you can copy values around however you like.
*p2 = 42; // x = The Answer

Zero size arrays in gcc c++ compiler

What will be the output of following c++ code snippet.
How we can assign a value to a variable which occupies no memory (0 bytes).Also doing pointer arithmetic gives false results(implied)
int main()
{
int arr[0];
arr[1]=1;
cout<<arr[1]<<endl;
cout<<sizeof(arr)<<endl;
int *p=arr;
int *q=p+1;
cout<<p-q;
return 0;
}
Sample execution ==> https://code.hackerearth.com/f8d7b1G
Well, actually, your code causes undefined behaviour.
Let's see how it works.
int a, b;
int c[2];
This will look like (imagine this as a stack inside the function "main"):
/***
+--------------------+ <- 0xXXXXXXXX (initial address)
| a |
+--------------------+ <- 0xXXXXXXXX + sizeof int (initial address + size of variable a) == &a
| b |
+--------------------+ <- ... + sizeof int (... + size of variable b) == &b
| c[1] |
+--------------------+
| c[0] |
+--------------------+ <- ... + number of bytes that are being allocated by the array (it's the pointer to the array)
== c or &c[0]
+--------------------+
| c (pointer) |
+--------------------+
// remember that compiler don't have to allocate it like this, it's just an example
***/
When you are trying to do something like this:
c[0] = 1;
it's being the same as:
*(c + 0) = 1;
because variable "c" contains the pointer to the array. It also explains why does a compiler allocate it reversely on the stack (because if we want to access a specific member of an array, we have to write something i did a little bit upper — *(c + N) = 1;, because STACK GROWS DOWN. If you still confused with it, write it in the comments below, i'll explain.
So, if you write something like this
int a, b;
int c[0];
It looks like:
/***
+--------------------+ <- 0xXXXXXXXX (initial address)
| a |
+--------------------+ <- 0xXXXXXXXX + sizeof int (initial address + size of variable a) == &a
| b |
+--------------------+ <- ... + sizeof int AND
+ number of bytes that are being allocated by the
array.
When the number of bytes is ZERO,
it equals to the address of the variable "b",
because &b + 0 == &b
+--------------------+
| c (pointer) |
+--------------------+
***/
So, we could say that what you are doing is:
*(&b + 1) = 1;
THIS IS VERY UNSAFE. Imagine there was no variable "b" or variable "a", you would just be managing something you shouldn't.
That's why zero-sized arrays are not allowed in C++ standart.

Understanding address of values and dereferecing pointers

I'm trying to have a better understanding of pointers and how they work. I'm also trying to understand the idea of dereferencing pointers. This is my understanding of pointers, and what may be possible of pointers.
This is a table representing 3 cells of memory. In each cell there is an address, cell name, and value.
+---------------------------+
| 1672 x | 1673 y | 5 |
| 1673 | 5 | 65 |
+---------------------------+
This is the initialization for the three cell blocks, assuming address 5 hasn't been initialized.
int x = 1673;
int y = 5;
This is how pointers are commonly used.
int* p_x = &x; //&x == 1672);
*p_x == 1673;
This would be true if you forgot to place ampersand in from of variable name.
int* p_y = y; //y == 5);
*p_y == 65;
If everything else is true would this also be true? If you were using the pointer to the address of x for a simple return on a member function, could you just skip a declaration of the pointer and just send the dereferenced address?
*&x == &y;
The lines with == will not compile, and are not actual c++ code. They are just an effective way to show equivalency.

Dynamic memory allocation and pointer non-char array type

I read this C++ tutorial, and in the pointers section there is a confusing example, exactly this:
double (*pVal2)[2]= new double[2][2]; //this will add 2x2 memory blocks to type double pointer
*(*(pVal2+0)+0) = 10;
*(*(pVal2+0)+1) = 10;
*(*(pVal2+0)+2) = 10;
*(*(pVal2+0)+3) = 10;
*(*(pVal2+0)+4) = 10;
*(*(pVal2+1)+0) = 10;
*(*(pVal2+1)+1) = 10;
*(*(pVal2+1)+2) = 10;
*(*(pVal2+1)+3) = 10;
*(*(pVal2+1)+4) = 10;
Is int (*pVal)[2] an array pointer?
I do not understand why is it allocating memory for double[2][2] but the *(*pVal2+1)+4) goes to 4?
Using the spiral rule:
+--------+
| +---+ |
| ^ | |
double (*pVal2)[2];
^ ^ | |
| +-----+ |
+---------------+
pVal2 is a pointer to an array of 2 doubles. Or, simpler:
using T = double[2];
T *pVal2 = new T[2];
The rest of the code leads to undefined behavior as: *(p + idx) is equivalent to p[idx], so *(*(pVal2+1)+4) is equivalent to pVal2[1][4].But the type of pVal2[1] is double[2], so there is no 5th element there...

differences between new char[n] and new (char[n])

Is there any difference between new char[n] and new (char[n])?
I have the second case in a generated code, g++ (4.8.0) gives me
ISO C++ does not support variable-length array types [-Wvla]
This makes me think if these two are the same or not.
new char[n] means "allocate n objects of type char.
does new (char[n]) mean "allocate 1 object of type array of n chars"?
deleting the first is clear.
should I delete the second with delete or delete[]?
are there any other differences I should be aware of?
may I safely remove the parentheses and turn the second case into the first, when other parts of the software expect the second?
The code is generated by a third party software (and used by other parts of the software), so I cannot just "use vector instead".
This is minimal example:
int main (void)
{
int n(10);
int *arr = new (int[n]); // removing parentheses fixes warning
*arr = 0; // no "unused variable" warning
return 0;
}
The basic issue here is that C++ does not allow an array bound [n] to be used in a type unless n is a constant expression. g++ and some other compilers will sometimes allow it anyway, but it's impossible to get consistent behavior when you start mixing variable-length-arrays and templates.
The apparent exception int* p = new int[n]; works because here the [n] is syntactically part of the new expression, not part of the type provided to the new, and new does "know how" to create arrays with length determined at runtime.
// can be "constexpr" in C++11:
const int C = 12;
int main() {
int* p1 = new int[C];
int* p2 = new (int[C]);
typedef int arrtype[C];
int* p3 = new arrtype;
int n = 10;
int* p4 = new int[n];
// int* p5 = new (int[n]); // Illegal!
// typedef int arrtype2[n]; // Illegal!
// int* p6 = new arrtype2;
delete[] p1;
delete[] p2;
delete[] p3;
delete[] p4;
}
Semantically, though, after any final [C] is used to convert a type into an array type, the new expression only cares about whether it's dealing with an array or not. All the requirements about type of the expression, whether to use new[] and delete[], and so on say things like "when the allocated type is an array", not "when the array new syntax is used". So in the example above, the initializations of p1, p2, and p3 are all equivalent, and in all cases delete[] is the correct deallocation form.
The initialization of p4 is valid, but the code for p5 and p6 is not correct C++. g++ would allow them anyway when not using -pedantic, and by analogy I'd expect the initializations for p4, p5, and p6 to also all be equivalent. #MM's disassembly supports that conclusion.
So yes, it should be a safe improvement to remove the "extra" parentheses from this sort of expression. And the correct deletion is the delete[] type.
new T[N] makes an array of N elements of type T.
new (T[N]) makes a single object of type T[N].
The effect is the same (and both expressions yield a T * that points to the first element of the array and needs to be deleted with delete[] (cf. 5.3.4/5), but clearly T[N] must be a valid type in the latter case, so N must be a constant expression, while in the former case it is a dynamic argument of the array-new expression.
new int [n]
//Allocates memory for `n` x `sizeof(int)` and returns
//the pointer which points to the beginning of it.
+-----+-----+-----+-----+-----+-----+-----+-----+------+------+
| | | | | | | | | | |
| | | | | | | | | | |
| | | | | | | | | | |
+-----+-----+-----+-----+-----+-----+-----+-----+------+------+
new (int [n])
//Allocate a (int[n]), a square which its item is an array
+----------------------------------------------------------------+
|+-----+-----+-----+-----+-----+-----+-----+-----+------+------+ |
|| | | | | | | | | | | |
|| | | | | | | | | | | |
|| | | | | | | | | | | |
|+-----+-----+-----+-----+-----+-----+-----+-----+------+------+ |
+----------------------------------------------------------------+
In fact both of them are equal.
Here is the code generated by assembler (just an ignorable difference):
int n = 10;
int *i = new (int[n]);
int *j = new int[n];
i[1] = 123;
j[1] = 123;
----------------------------------
! int *i = new (int[n]);
main()+22: mov 0x1c(%esp),%eax
main()+26: sub $0x1,%eax
main()+29: add $0x1,%eax
main()+32: shl $0x2,%eax
main()+35: mov %eax,(%esp)
main()+38: call 0x401620 <_Znaj> // void* operator new[](unsigned int);
main()+43: mov %eax,0x18(%esp)
! int *j = new int[n];
main()+47: mov 0x1c(%esp),%eax
main()+51: shl $0x2,%eax
main()+54: mov %eax,(%esp)
main()+57: call 0x401620 <_Znaj> // void* operator new[](unsigned int);
main()+62: mov %eax,0x14(%esp)
!
! i[1] = 123;
main()+66: mov 0x18(%esp),%eax
main()+70: add $0x4,%eax
main()+73: movl $0x7b,(%eax)
! j[1] = 123;
main()+79: mov 0x14(%esp),%eax
main()+83: add $0x4,%eax
main()+86: movl $0x7b,(%eax)
You must delete both of them by delete [] ...