gizmo_bob
gizmo_bob

Reputation: 3

Pointers, ampersands and pointers again?

I just cannot get my head around pointers - when and how exactly I am supposed to use pointers. No matter how many videos and literature I read on it, I just do not understand when and how I am supposed to use it. I am that dense apparently...

Let us look at this example here:

int main()
{
    int* ptr = new int(10);

    std::cout << ptr << std::endl; 
    std::cout << *ptr << std::endl; 
    std::cout << &ptr << std::endl;
}

This would return us a value like this:

000001A68ABB5250 
10 
000000455ADCF828

What do these outputs mean? The first and the third look like they are address number in memory but they are different and I do not understand what are they supposed to do? The second one is the value I assigned but why do I have to get it with a pointer symbol?

Let us have a look at this:

int main()
{
    int ptr = 10;

    std::cout << ptr << std::endl; 
    //std::cout << *ptr << std::endl; //gives "Error(active)    E0075   operand of '*' must be a pointer but has type 'int'"
    std::cout << &ptr << std::endl;
}

Which respectively returns:

10
000000D9242FF5F4

What makes the code above different to this example code? Why don't I have to use pointer to get the value? Ampersand show us the memory address right?

Essentially can someone explain to me in the silliest, easiest to understand way what is the point of pointers and why do we need them in C++.

Upvotes: 0

Views: 373

Answers (6)

Some programmer dude
Some programmer dude

Reputation: 409176

If we "draw" the first example it will be something like this:

+------+     +-----+     +--------------+
| &ptr | --> | ptr | --> | *ptr int(10) |
+------+     +-----+     +--------------+

So ptr is pointing to an int value (initialized 10 10). And &ptr is pointing to the variable ptr.


In the second example the variable name ptr is misleading since it's not a pointer. It's a plain int variable.

Drawn it will be like

+------+     +-------------+
| &ptr | --> | ptr int(10) |
+------+     +-------------+

Upvotes: 1

pm100
pm100

Reputation: 50180

The real interesting question is 'why pointers?' if you get that then the rest might follow.

Side note, C++ has many constructs which make a lot of this 'hidden', but what I describe is still going on under the hood

There are several scenarios where pointers are useful

#1 Passing Big things around

Imagine you have this

struct Biggy{
     int Widle[500];
     char Froodle[500];
}

This is a big object, expesive to copy, youhave a function that operates on Biggy objects, say it compares the Widle and Froodle arrays.

bool AreTheSame(Biggy b) {....}

The way that c and c++ work is that arguments are passed by value, that means they are copied from the caller to the called function

So when I do

Biggy b = MakeAndFillBiggy(); // we will come back to this function
bool same = AReTheSame(b);

all 1000 bytes are copied, this is expensive

But if I have instead

 bool AreTheSame(Biggy *b) {....}

then I can do

bool same = AretheTheSame(&b);

now I am just passing a pointer. Thats 4 or 8 bytes not 1000.

#2 Allow a function to modify its arguments

As pointed out

bool same = AreTheSame(b);

Passes a copy of b to the function, this is expensive, but also if the function wants to change b it cant. All it has is a copy.

Imagine we had a function MakeTheSame(Biggy b); that looks to see if the two arrays are the same and if not it schanges them, great. But like this its useless as its changing a copy, not the original.

Again we can do

void MakeTheSame(Biggy *b) {..}
 ...
Biggy b = MakeAndFillBiggy(); // we will come back to this function
bool same = MakeTheSame(&b);

now we are passing a pointer to the callers 'b'

Note that this is useful for any size object.

You have probably seen this

 int num;
 scanf("%d", &num);

we have to pass the pointer to num so that scanf can modify it.

#3 To get and use dynamic memory

YOu have an app that keeps a list of football teams for a tournament.

struct Team{
   string name;
   string town;
   string captain;
}

How big does the array that holds it need to be? YOu dont know in advance how many teams will enter.

YOu could have

 Team teams[100];

But thats a bit wasteful. So instead you use dynamic memory, every time you get a new time you allocate a new Team object on the heap.

Team *team = new Team();

'new' allocates memory dynamically from a pool , it will be big enough for a Team object and it will be initialized. It returns a pointer to this new memory.

Now you can have just the right amount of Team objects.

Lets go back to this

Biggy b = MakeAndFillBiggy(); // we will come back to this function

Now we will do

Biggy *MakeAndFillBiggy(){
    Biggy * b = new Biggy();
    ... fill arrays somehow
    return b;
}

and now we do

Biggy *b = MakeAndFillBiggy();

C++ goodies

C+ has a thing called references. These behave like pointers (cheap to pass, allow functions to change their arguments) but syntactically look like non pointers.

Eg

void MakeTheSame(Biggy &b) {..}    

this says that MakeTheSame takes a referenc as an argument. Now we can do

Biggy b;
MakeTheSame(b);

There are some advantages to references over pointers but they do exactly the same thing

C++ has 'smart pointers'

The problem with our function

Biggy *MakeAndFillBiggy(){
    Biggy * b = new Biggy();
    ... fill arrays somehow
    return b;
}
Biggy *b = MakeAndFillBiggy();

Is that it retunr a pointer to an object on the heap. When we have finished with it we have to realease its memory back to the pool so it can be reused.

so somewhere there has to be

delete b;  

The problem is that in complex code it might be hard to knwo when you have finished with b. Enter 'smart pointers' they detect when you have finished with the memory and release it for you

so you will see

std::shared_ptr<Biggy> MakeAndFillBiggy(){
    std::shared_ptr<Biggy> p = std::make_shared<Biggy>();
    ... fill arrays somehow
    return b;
}
std::shared_ptr<Biggy> b = MakeAndFillBiggy();

or more succintly becuase the compiler is smart

std::shared_ptr<Biggy> MakeAndFillBiggy(){
    auto p = std::make_shared<Biggy>();
    ... fill arrays somehow
    return b;
}
auto b = MakeAndFillBiggy();

under the hood exactly the same thing is happening, theres just some glue added on top to keep track of when this pointer is not longer needed

Inside there was still ptr = new xxx and a *p

Upvotes: 0

Andrew Ealovega
Andrew Ealovega

Reputation: 11

In breif:

int* ptr = new int(10);

ptr (the int) is litterally just the memory address (a number) of the int you allocated on the right.

std::cout << ptr << std::endl;

then is printing out the memory address of the new int. This is because ptr is just a number that happens to be a memory address.

std::cout << *ptr << std::endl;

Here you tell C++ to access the memory address with the number that ptr is. Because ptr is the memory address of the int, you will get the int's value.

std::cout << &ptr << std::endl;

Finally, you tell C++ to give you the memory address of ptr. If you used *&ptr, then you would simply get the same as your first print statement.

As far as the second half, the int is simply a value here, not a pointer. When C++ says you cannot use *, it is preventing you from accidentally treating the int as a memory address. If you really wanted to treat the int as a memory address however,

int* x = 10

would then allow *x to compile. Now because this is accessing an int at memory address 10 (which is not part of your program) your OS will almost certainly kill your program.

Upvotes: 0

Asphodel
Asphodel

Reputation: 190

I'm not much of an explainer, but general overview is:

After you declare and define int* ptr = new int(10);

When you use ptr, it is the name that is of type int*, like for int x = 5, when you use x, it is of type int.

ptr is a pointer so, it is a variable that stores in address, so when you print it, it will be an address.

&ptr will be the address of a pointer, yes pointers have addresses too. Basically, it is of type int**, so when printed will be an address too.

*ptr dereferences the pointer, so it will get the value 10 that is stored in the memory location pointed by the pointer. Think of it as, every address points to a memory location that stores a value.

If it is just plain int ptr = 10, you can't dereference it: apply *ptr because it is of type int and not a pointer of int*.

However, if you apply &ptr to it, it will get the address of int ptr which is essentially of type int*.

For example, if you do: int x = 5; int *ptr = &x;, If you dereference ptr, and do: cout << *ptr, it will print 5.

I had a hard time with pointers first time around too. It's alright. Took me a month or so to wrap my head around it. In fact, I still have things I'm boggled by with pointers. Oh well, I'm not very good at explaining, and it's probably about the same as what you read...wish you luck.

Upvotes: 0

HolyBlackCat
HolyBlackCat

Reputation: 96166

The first thing to understand is that the meaning of symbols * and & depends on context.

In int* ptr, * means "this is a pointer".

But otherwise (e.g. in cout << *ptr), * means "dereferencing" a pointer. I.e. given a pointer to X, * returns X, whatever that happens to be.

& (e.g. in cout << &ptr) "takes an address" of something. Given X, it gives you a pointer to X.

& and * (in the second sense) are the opposite operations, they cancel each other out.

int* ptr = new int(10); is somewhat similar to int x = 10; int *ptr = &x;, which in turn means int x = 10; int *ptr; ptr = &x; (not *ptr = &x, since * means a different thing here, see above).

So, given int x = 10;, &x is a "pointer to x" (printing it would give you an address). After you do int *ptr = &x, ptr is also a "pointer to x", so printing it would show the same address.

*ptr gives you x, just like *&x (which means *(&x)) does, since * and & cancel each other out.


Pointers can point to other pointers. &ptr gives you a "pointer to ptr", or "pointer to pointer to x", which has type int **. **&ptr would thus return the value of x.

Upvotes: 0

alecbeau
alecbeau

Reputation: 41

When I first started programming, pointers were one of the things where I just accepted they work and eventually built up enough experience to learn/understand them.

In one of my classes, a professor said it is best to use pointers when you want to modify an object but not make a copy of it. Passing by value to functions creates a copy of the object being passed inside of it.

Upvotes: 0

Related Questions