Skip to content

Instantly share code, notes, and snippets.

@Eolu
Created March 15, 2020 18:45
Show Gist options
  • Save Eolu/334435a88c578c9e7f670ef7e56159ac to your computer and use it in GitHub Desktop.
Save Eolu/334435a88c578c9e7f670ef7e56159ac to your computer and use it in GitHub Desktop.
Some pointers on C pointers (and C++ references) for new developers trying to grasp the essentials.
// Note: I use hex nomenclature (0x...) to make it obvious that the value I'm talking about
// is a memory address. Under the hood there isn't any particular distinction (everything is
// represented in binary eventually).
void pointerception()
{
// i is not a pointer.
int i;
// i can be set to a value
i = 8;
// Let's say that i has a value of '8' and is stored at memory address 0x04.
// Let's see how we can access these things:
// This will print 8
printf i;
// This will print 0x04
printf &i;
// Those are the only 2 valid ways we can access i. Let's delve deeper now:
// p is a pointer
int *p;
// p can be set to a value also, but this value will be treated as a memory address.
// this line means that p is the null pointer
p = 0;
// We can instead set p to a REAL memory address
// this line means that p is a pointer to i
p = &i;
// If we just print p, we get the memory address.
printf p; // This will print 0x04
// However, we can dereference p to get the value.
printf *p; // This will print 8
// If we tried to do the above line while p = 0, we would've gotten a null pointer exception
// Homework question: What happens if I do this?
printf &p;
// Now let's use some of the functions defined below.
//*************************//
// **** PASS-BY-VALUE **** //
//*************************//
// This function will make a copy of the value given to it. If we pass in 'i', it's just got a local
// variable with a value of 8.
valueFunction(i);
valueFunction(*p); // This will also pass in an 8.
// These things are legal but probably mistakes:
// I can pass in just p, but it will just convert the memory address to an int (so I'm passing in 4)
valueFunction(p);
// Same thing if we dereference i. This will pass in a 4 (probably not what you want)
valueFunction(&i);
//*****************************//
// **** PASS-BY-REFERENCE **** //
//*****************************//
// NOTE: C++ only. This doesn't exist in C.
// This function will create a reference to i, NOT a copy. That means that modifying the variable
// inside the function will directly modify the value of i.
referenceFunction(i);
// This is a bit odd, but maybe occasionally done. We could dereference p inside this function to
// access or modify the value it points to, OR we could directly change the value of p inside this
// function to make it point to something else. Ultimate power and ultimate responsibility.
referenceFunction(p);
// This is equivalent to passing in i (assuming p is a pointer to i, as it is currently)
referenceFunction(*p);
//****************************//
// **** PASS-BY-POINTER? **** //
//****************************//
// Note: there's nothing really special about "pass-by-pointer". This is just pass-by-value
// except the value will be a pointer to the memory address of whatever is passed in rather
// than the value it points to. While this CAN be used to modify the value in the caller (as we
// did with a reference), it truly does create a local (pointer) variable in the function (unlike a
// reference). To put it simply, passing i into `valueFunction` creates a new variable with a value
// of 8, and passing i into `pointerFunction` creates a new variable with a value of 0x04.
// The function gets a pointer to i (so it has its own variable just like this function has p)
pointerFunction(i);
pointerFunction(*p); // This is effectively the same as above
// The function gets as pointer to p. That's a pointer to a pointer. To actually get to the value
// of i it would have to dereference twice (so **pnt).
pointerFunction(p);
pointerFunction(&i); // Same thing here, except we can't modify the value of this function's p
}
void valueFunction(int val)
{
// I can do things with val and I'll never affect anyone outside my scope!
}
// This is valid in C++. This does not exist in C
void referenceFunction(int& ref)
{
// If I modify ref, I modify the same variable the caller passed in!
}
// This is considered oldschool. References are now the preferred way
void pointerFunction(int* pnt)
{
// If I dereference pnt, I can access the same memory address that the caller had access to.
// But if I modify the value of pnt, I make it point to a new memory address and am no longer
// (necessarily) modifying data from the calling function.
}
// It's legal to do crazier things, like have a pointer to a pointer like this:
void pointerPointerFunction(int** pnt) { /* ... */ }
// Or even a reference to a pointer:
void pointerReferenceFunction(int*& pnt) { /* ... */ }
// If you work with enough code from various places, you will undoubtedly run into one of these
// things at some point (or something semantically similar). While they may look odd, they aren't
// doing anything special.
/*
* At the end of the say, (almost) all of this is just syntactic sugar.
*
* In assembly there are no pointers or references. We can treat any value as a memory address,
* and its up to us to only dereference real memory addresses lest we break something by trying
* to dereference a value.
*
* In C there are pointers but not references. The existence of the asterisk in a type (eg `int* p`)
* defines a pointer, and it exists so YOU can tell that this is a memory address rather than a
* value. There's nothing stopping you from storing a memory address in a type without the asterisk
* and dereferencing to get the value it points to it (save for a compiler warning). The only "not-
* just-syntactic-sugar" construct is the &, which returns the memory address of a variable (like we
* did when we wrote `p = &i`). We can also put the `*` in a function declaration, which effectively
* means "automatically create a pointer to whatever I pass in".
*
* C++ keeps all of this but later added something very convenient: references. These (perhaps somewhat
* confusingly) use the `&` symbol also, but this time in a function declaration instead of in the middle
* of an expression. Whereas pass-by-value says "Create a variable based on a value", and pass-by-pointer
* says "Create a variable based on the memory-address of a value", pass-by-reference doesn't truly "create"
* anything. It's really just like saying "Give this function access to this variable, just as if it were in
* the same scope with no particular copying or dereferencing". (This isn't the WHOLE story, but it's enough
* to be able to use references effectively. I'll leave it as an exercise to figure out where I've somewhat
* misrepresented what's going on here).
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment