Pointers in C
In this lesson, you’ll learn how pointers work in the context of C programming. The code example for this lesson will be in C, so you’ll see how creating a variable and assigning a value to it works differently than it does in Python. You’ll also cover the differences between passing by value and passing by reference.
00:00 In this video, I’m going to explain the concept of pointers in the context of C programming. That means, for the time being, forget all about Python. You’ll learn about how this applies to Python in the rest of this course.
00:15 First, I’ll show you pointers in C code. And then, in the next video, I’ll explain the concept with a colorful analogy. This right here is a little snippet of C code.
00:27
The #include
line at the top tells the C compiler that we want to use a function defined in the standard io library, notably, printf
on line 5.
00:38
int main()
is the main function in C. It’s the entry point for our program. By returning zero here, we tell the OS that this program ran successfully.
00:51
All the code in between the curly braces is what will actually run line-by-line. On line 4, I create a new integer variable called x
, and I tell it to store the value 123.
01:06
This looks pretty similar to Python, except we manually specify the data type of the variable before the name, and we end the statement with a ;
(semicolon).
01:16
What we’re really doing here is telling C to allocate four bytes of memory to store an integer. Then we store the integer 123 in that memory space. C then assigns the name x
to that space in memory.
01:33
We can say that x
owns that space, so anytime we use x
, we should get 123.
01:41
The next line simply prints the value of x
. You can tell because the second argument passed to printf
is our x
variable.
01:52
C code needs to be compiled and linked before it can be run as an executable. To do that, I’ll use what’s called a makefile, which allows me to obtain an executable just by running the make
command in the terminal.
02:09 If you’re not familiar with makefiles, just run this command right here, and you should be all set.
02:16
Now I can run the program with ./main
and we get 123, just as expected. What’s really happening under the hood here is a little bit more advanced.
02:29
When we pass x
to the printf
function, we’re really creating a copy of that x
variable’s value and passing it in.
02:37
This is called passing by value because we’re passing a copy of the value 123 to the printf
function. To better understand this concept, take a look at this code.
02:54
Here, I have defined a new function called change_variable
.
03:00
This function will take an integer variable and then increment its value by 10. void
means that the function returns no data. In the main function, I call change_variable
, passing in our x
variable, which stores the value 123.
03:19
Then, just as before, I print out the value of x
. Take a moment to pause the video and try to predict what’s going to be printed. The answer might surprise you.
03:34 I’ll compile this and run it.
03:39
And it looks like we’re still seeing 123. It’s as if our new function didn’t do anything at all. What really happened is this: we created a new integer variable, then we passed it to the change_variable
function.
03:56 This function received a copy of the variable, and so a different four bytes of memory was used to store the copy. Then, we increment the value stored in this new four bytes of memory by 10.
04:11
At this point, the change_variable
function ends, and with it, our input variable here, with a value of 133, control returns to the main
method, and because we never actually changed the original four bytes of memory containing 123, that’s what gets printed.
04:31
This is a common problem when passing by value. One way to get around this is by changing the return type of the change_variable
function to int
.
04:43 Now, we can simply return the value stored in input.
04:48
And we’ll have to reassign x
down here too, so it holds the value returned by our function. Let’s compile and run this and see if it works.
05:03
Great, now we’re seeing 133. What we’ve done here is make sure the data in the new four bytes of memory that belongs to the change_variable
function is copied back into the old four bytes.
05:17 This is fine for a simple four-byte integer, but think about what would happen if this were a variable that used hundreds of bytes of memory. If we pass that by value to a bunch of different functions to manipulate and use, then we’d be copying hundreds of bytes of memory over and over again, which is very computationally expensive and might create problems if we’re running this program on something like a microcontroller, with very little memory.
05:47 Instead, I’ll modify this program to do what’s called passing by reference. To do that, I’m first going to undo the recent changes I made. Now this function won’t return anything, just like we had it before.
06:05
I’m going to change the input parameter from int
to int*
. This is called an integer pointer. A pointer is simply a memory address to the contents of a variable.
06:20
Instead of the change_variable
function taking an integer value to manipulate directly, it’s going to take the memory address to an integer value.
06:31
That means that input is no longer the value 123. Instead, it’s the memory address to wherever 123 lives in memory. In order to actually access the data at the provided memory address, I’m going to append another *
(“star”) to the beginning of the variable name right here.
06:54 This is called the dereference operator because it dereferences a pointer to get at the data it’s actually pointing to. In other words, it looks at the value stored at this memory address.
07:09
All that’s left to do now is modify the code inside the main
function. In its current state, the code won’t actually work. And that’s because we’re supplying the value 123 to the change_variable
function, which is asking for a memory address, not an actual value.
07:27
So let’s give it a memory address instead. We can do that by using the reference operator, which is the &
(ampersand). Appending this at the beginning of a variable will get the memory address of that variable’s value, instead of the value itself.
07:46
So now we’re passing the memory address of x
into change_variable
, who will dereference it and change its value.
07:56
Notice how we don’t need to return anything here. That’s because we’re now passing x
by reference, so we’re allowing the change_variable
function to change the value stored at x
’s memory address directly.
08:11 Technically, we are still passing a copy of a pointer by value, which is eight bytes. This means that no matter how big the data pointed to by the pointer, we’ll only ever have to pass eight bytes of memory between functions.
08:28 That’s a lot better than copying potentially hundreds of bytes. I will compile and run this,
08:37 and it would probably help if I save the code first.
08:42 There we go. Functionally, this looks the same as before, but now we’re passing by reference. There’s a lot more I can say about memory management and pointers, such as dynamic memory allocation, but this is all you need to know for this course.
09:01 If you’re still confused as to how pointers can be used to pass by reference, I’ll explain it with a real-world analogy in the next video. Hopefully, that helps to clear things up.
Become a Member to join the conversation.