Overview
Pointers are one of the simplest but also most challenging concepts in the C language. Some of this challenge is related to the syntactic pieces of required to create and use pointers. Often we have found that learners approaching pointers for the first time would create a large number of rules that apply only to pointers to make them "work". Ultimately, these rules backfire and become manageable.
When working with pointers we must remember that they are just variables. We need to use them as we would any other variable without creating unnecessary rules. They act like any other variables. What we need to do is understand what we are saying in our code when we work with pointers and understand that we are simply applying the same rules as every other variable we have used.
What exactly are pointers?
Pointers are simply variables that happen to hold a memory address. That's pretty much it. The variable at the memory address stored in a pointer is often referred to as the variable being pointed at. When we draw diagrams we will often draw a pointer variable's value as an arrow that points to another variable somewhere.
At the memory addressed stored in a pointer is a variable of a certain data type that matches the pointer's type. All addresses are the same size (same number of bytes needed to hold an address) and thus all pointers regardless of what it points to are the same size.
Why pointers?
There are several useful things we can do with pointers.
- Pointers are used to pass large aggregate types to funcitons
- If a function is suppose to work with structs, we should pass the address of the struct to a function. No matter the number of data members in our struct, the pointer is a relatively small variable. regardless of the size of the struct, the address is the same. You can think of this like a street address to a house. The address is typically
<number> <street name>. at the actual address there might be a little house or a large house. - When we pass an array to a function, we are providing the starting address of the array. The name of the array is actually the address of the first element of the array. Inside the function, we can figure out where the second element will be due to the fact that the second element is beside the first.
- If a function is suppose to work with structs, we should pass the address of the struct to a function. No matter the number of data members in our struct, the pointer is a relatively small variable. regardless of the size of the struct, the address is the same. You can think of this like a street address to a house. The address is typically
- Pointers allow us to provide multiple results
- Suppose I was to give a function a total amount of time in seconds and wanted the number of minute/seconds in that amount of time. This is done using / and % operators. However, the issue is that a function can only return one value. we can return the hour component or the minute component but not both... Using pointers, we can actually write functions to provide multiple results from a function
Declaring a pointer
typeName* variableName;
The typeName is the name of the data type the pointer can point at. variableName is the name of the pointer.
Example:
int* intPtr;
intPtris the name of the variable.intPtrcan store the address of anint- the
*is what makes intPtr a pointer and not a regular integer.
Address-Of Operator
Previously we have used the & (address-of) operator when we used scanf. What we were doing was sending the location(address) of the variable to the scanf() function.
Pointers store addresses. Thus, to make a pointer "point" to a variable, we simply need to assign to the pointer the address of the variable:
Syntax:
dataType someVariable;
dataType* ptr = &someVariable;
in the above, we carete some variable of a certain type. To store the address of that variable we need a match pointer declaration. We can then use the & operator to access the location of that variable and store it into ptr.
Example:
int x; //x is an integer variable
int* ptr = &x; //ptr is an integer pointer
//we are storing the address of x into
//ptr in this example.
While all pointers take the same amount of space in memory, they typically have a data type. The data type doesn't alter the amount of space needed to store a pointer. Instead it tells the compiler how to interpret the data at the other end of the pointer
Accessing data through the pointer
Pointers store addresses. We are rarely interested in the address itself. We are interested in the thing at that address. This is similar to addresses in real life. Suppose you want to go shopping at a new store. You might lookup the shop's address... but you are not actually really interested in that address except as a means to get you to the shop itself. The address's value is in the fact that it can help you locate the shop, it is not usually useful on its own. 123 main street as an address is not useful.. except for the fact that it tells you where you can find the shop.
To access the variable at a particular address we need to use the * operator. Yep the same symbol we used to say that variable is a pointer is used to dereference a pointer. This operator lets us access the variable itself.
Example:
int x = 15;
//ptr points to x. x currently has value of 15
int* ptr = &x;
//this will dereference ptr and print the value ptr is
//pointing at.
//this will print 15
printf("%d\n", *ptr);