Odd request. Sorry :(

Hey folks I'm hitting the brakes on my C++ progression and going back over some of the basics that I was either not fully competent with or feel like I missed some things. One thing I've always struggled with was pointers and what they're actually doing. Sure I've used them ONLY because I knew I had to, though I wasn't sure WHY I had to do it, just knew I had to, to make the syntax correct and get on with the show.

That being said, here is my odd request. When I learned classes it never really clicked until someone made the analogy "Think of a car. It has tires, a body, color, etc, etc, but all of those things can be quite different from car to car. A class will store all of those different parts allowing you to assign and manage variations of those parts to DIFFERENT cars. Car 1 might be blue while Car 2 might be red"

Now I may have regurgitated that analogy wrong, my point is that when classes were used to relate to other real world counter parts, it suddenly made sense.

So again, I'm making a rather odd request. If possible could someone make an analogy for pointers relating to something else in the tangible real world? I know they point to an address in memory but what does this REALLY mean?
Oh God no please don't.

Analogies in programming exist when the person explaining it is struggling to explain. Pointers are very simple and absolutely do not need an analogy to explain them.

I know they point to an address in memory but what does this REALLY mean?


Imagine that every memory location has a unique address. This is true. Each address is a number. This is also true.

With me so far?

Take that number, and store it in a variable, in much the same way that we might store the number "3" in an int.

The variable we store that number in (the number being the address of a piece of memory somewhere) is called a pointer. It's just a variable type intended for storing a memory address, in the same way an int is intended for storing a number.

That's it. Done. No need for an analogy. What of this is not clear?
Last edited on
I still don't understand that or why it's needed. Why are we pointing to an address in memory instead of just accessing what's in that memory directly?
why it's needed


Right, that's a different question, but is the first bit clear? Do you get what a pointer is? It's a variable type, intended for storing a memory address. It's usually the same size an an int, because all it does is store a number. The number is the unique number of some piece of memory somewhere.

There is no analogy that will make that clearer; it's a data type, intended for storing a memory address, which is just a number.
Last edited on
So in this code, what exactly is going on?

 
int *i = new int(5);

int* means create a new object of type "pointer-to-int". So what is a pointer? It is a variable type intended for storing a memory address. So this is an instruction to create a variable that is for storing a memory address in. This comes with additional information; we are telling the compiler at this stage that the memory address we put in here is going to be the address of a piece of memory containing an int. This is not completely necessary, as a memory address looks the same no matter what kind of object is in that memory, but it's a big help all round knowing what we meant to do with it.

i We're calling this variable, this pointer, i.

new Make me an object on the heap. The heap is a big chunk of memory that I can use. I want to make an object on that big chunk of memory. When you've finished making it, give me back the address of the memory that you made the object in. That's how new works. You get back the address that the object has been created in.

int(5) This object I want you to make me is an int, set to the value 5.

So, new gives us back a memory address. We need to write that memory address down somewhere, so that we can access the object at that memory address, which in this case is an int. If we don't know where that memory is, we can't get the int in it, so we need to write that memory address down.

If only we had some way of storing memory addresses. Wait, we do. We have a variable type designed for storing memory address. Pointers. So we store this memory address in the pointer named i.

So when we've finished, i holds a number. If we read that number, and then go and look at the corresponding piece of memory, we'll find it has an int in it, of value 5.
Last edited on
So why would would someone use a pointer to access that integer with a value of 5 when you could just do int i = 5 and just access it directly with i instead of a pointer?

I guess I'm struggling to see why we want to access these things through memory when we can do it directly.
Last edited on
For a number of reasons.

Firstly, the stack is small. Objects you make like this (i.e. without using new, or malloc if you've reverted to C):
 
int x = 5;

go on the stack. The stack is a section of memory. It's small. Much much smaller than the heap. Sometimes just a few megabytes. So if you want to use more memory than just a few megabytes, you need to put your objects on the heap, and to use the heap you have to use new, and new gives you back a memory address. You can't "access it directly".

Additionally, the compiler needs to know the size of things that will go on the stack at compile time. If you don't know how big something is going to be (like an array which might hold 5 items and might hold 50, depending on something the user does) it can't go on the stack. I will say that C99 allowed unknown size arrays on the stack, but it's an easy way to cause trouble. Don't know the size of something at compile time? Put it on the heap. It's what it's for.

Secondly, and not something everyone runs into, real-world hardware at the low-level works on memory addresses. The hardware manual tells programmers which memory address to put their data into so that the hardware can find it, and which memory addresses to read when the hardware has data for them. When all you have is a memory address, all you can use is a pointer.

Thirdly, it allows you to pass a parameter by pointer. You can pass a function the memory address of a variable, and it can then work directly on that variable, and changes it made to the variable persist after the function returns. Like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void some_function(int* i, int* j, int* k)
{
  // increment them all
  *i = *i + 1;
  *j = *j + 2;
  *k = *k + 3;
}

int main()
{
  int i=0, j=0, k=0;
  some_function(&i, &j, &k); // &i is the memory address (so a pointer to) i
  
  // here, i=1 and j=2 and k=3. If we didn't pass pointers to the variables,
  // the changes would have happened to copies instead of the originals
}

In C++, we would prefer to do this with references rather than pointers, but there are a couple of things we can do with a pointer that we can't do with a reference.


Forth, it allows polymorphism. I can talk more about that if the first few are clear, because that one is a bit more complicated.
Last edited on
Ah yes that example and explanation did it. Sorry for being difficult but your patience and persistence has paid off! Makes a lot more sense now. All in all it's more efficient, and allows you to do more with it if we can access it in memory.

Thanks again sir for sticking with me :)
Last edited on
In C programming, pointers are handy when we have large objects (5MB say), rather than copy the whole thing, we can use pointers instead. We can assign a pointer (8 bytes) to something, rather than copy 5MB. This is particularly handy for things like sorting, we can swap the pointers rather than the large object.

With built in types like int, double, char etcetera , it generally isn't worth having a pointer for them, it's just as quick to deal with the variable itself. However, if one has their own type (a struct or class) then it is worth having a pointer or reference to it.

C++ has the STL which does all the hard work for you, often there is no need to use pointers at all. However it is good to learn pointers though, so one can at least have an appreciation of what might be happening under the hood, or if one has to do actual C Programming.
No analogies in sight. That's a relief.

When you get into inheritance, you'll see people using pointers again, for polymorphism. Don't miss that.

In C++, you shouldn't pass a pointer-to-an-object when you could pass a reference-to-that-object, but it does illustrate the concept.
Last edited on
So out of curiosity and working on what TheIdeasMan said, a pointer to an object (in the example below, a class) should be smaller size than the object itself, the following code returns 36 bytes for the size of the pointer to an object as well as the object itself. Should the pointer itself return a smaller size?

1
2
3
4
5
	Account *newacc = new Account(name, accnum, balance);
	Account e1;

	std::cout << sizeof(*newacc);
	std::cout << sizeof(e1)
Neither of those output numbers is the size of a pointer.

std::cout << sizeof(*newacc);

*newacc is the object that the pointer is pointing to, which is an object of type Account.

std::cout << sizeof(e1)
e1 is an object of type Account.

You have output the size of two account objects. You have not output the size of a pointer.

Here is where you create the pointer: Account *newacc
so the pointer is named newacc, so here is how to output the size of that pointer:
std::cout << sizeof(newacc);


When you've rewritten it and you get the size of a pointer, make pointers to various different objects of different size, and observe that the pointers are all the same size.

I expect a pointer to be 8 bytes on a typical 64 bit system, and 4 bytes on a typical 32 bit system, but it's certainly common to have 4 byte pointers running around in code compiled on a 64 bit system.
Last edited on
Indeed you're correct. sizeof(newacc) returns a size of 4. I am running a 64bit system but you did say it's common that they will still be 4 bytes in size.
Yes. You should be able to insist to your build that you want 8 byte pointers (so you can address more than 4GB of memory), but unless you really need it, I wouldn't be concerned (and if you need it, you'll find out). You can also get some weird and whacky pointer sizes on various systems and with various edge cases, but it's something that you'll probably never need to care about.
@Moschops Very well. Thank you again for your input and explanations. Much appreciated.
Pointers provide a level of indirection. Let's say you have three classes: Book, Library, and Person.

A library has books that people can borrow. The books don't actually belong to the people who borrow them, the books belong to the library. If a person rips the pages of a borrowed book, they're damaging the library's property.

Just for completeness, consider the following example (ignore the lack of error-checking):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <iostream>
#include <string>

class Book {
	std::string title;
	bool hasRippedPages;

public :

	Book(const std::string _title = "Title") : title(_title), hasRippedPages(false) {}

	std::string getTitle() const { return title; }
	void setTitle(const std::string _title) { title = _title; }

	bool getHasRippedPages() const { return hasRippedPages; }
	void setHasRippedPages(const bool _hasRippedPages) { hasRippedPages = _hasRippedPages; }
};

class Library {
public :

	Library() {
		books[0].setTitle("Pointers: A Memoir");
		books[1].setTitle("Foo vs Bar");
		books[2].setTitle("C++ Cookbook");
	}

	const static int num_books = 3;

	Book books[num_books];
};

class Person {
public :

	Person(Book* _borrowedBook) : borrowedBook(_borrowedBook) {}

	Book* borrowedBook;

	void ripPages() const {
		borrowedBook->setHasRippedPages(true);
	}
};

int main() {

	//A library exists
	Library library;

	//Bob is a Person, and he borrows one book from the library
	Person bob(&library.books[0]);

	//Bob rips the pages of the book he's borrowing
	bob.ripPages();

	//Now, Bob (and, incidentally, the library) has a book with ripped pages
	for (int i = 0; i < Library::num_books; ++i) {
		bool ripped = library.books[i].getHasRippedPages();
		std::cout << "The book \"" << library.books[i].getTitle() << "\" has " << (ripped?"":"no ") << "ripped pages." << std::endl;
	}

	std::cin.get();
	return 0;
}
Last edited on
Topic archived. No new replies allowed.