<< Prev Next >>
What are pointers
For most new programmers, one of the most confusing things is pointers. It actually is a very simple concept. Let's look at an analogy: email addresses. Your email address is unique, which means no other one has the same email address. This also means emails sent to that email address will be correctly delivered with no confusion. Well, a pointer is also an address, but an address to main memory (RAM).
Main memory is a set of cells where each one has a unique address, represented by a number. Memory addresses (also called "pointers") start at 0 and go as high as the hardware allows. For example in x86-32, maximum memory allowed is 4 GiB, which means the highest memory address is 4,294,967,295 (2^32 - 1 ). Pointers (which are memory addresses, yes I know this is boring, but this must be incrusted in your mind) are usually represented using hexadecimal notation. I want to stress that hexadecimal are not different numbers from decimal or binary. All of them are the same numbers. It's only the way we represent the value that changes. So the previous highest memory address in x86-32 architecture would be written like 0xFFFFFFFF in hexadecimal (the leading 0x is just to denote that the following number is written in hexadecimal).
In previous lesson we already used pointers. Character strings are actually pointers. Let's check this:
- Code: Select all
#include <stdio.h>
int main()
{
char* example = "Hello my friend!\n";
printf("%d, %x\n", example, example);
return 0;
}
In this code you can see how we're printing the real value of example, and not what it points to. That is, we're printing the actual value of the memory address and not the content of that memory address. Running this yields this result (for me, for you the address will most likely be different)
- Code: Select all
4206692, 403064
Same value but the first number is written in decimal while the second is the same but in hexadecimal.
Ok so now we're going to do a nasty trick. We're going to assign this address to another pointer and print the contents. Of course this will print the "Hello my friend!\n" string because it's the data that resides at that address (remember an address is unique).
- Code: Select all
#include <stdio.h>
int main()
{
char* example = "Hello my friend!\n";
printf("%d, %x\n", example, example);
char* example2 = 0x403064;
printf("%s\n", example2);
return 0;
}
(Remember to substitute 0x403064 by the address your execution showed!)
This shows:
- Code: Select all
4206692, 403064
Hello my friend!
This is of course equivalent of doing char* example2 = example; and as we know this don't mean we're copying the string, but the address of the string. And the address being a number (an int in x86-32) we can have more fun, like:
- Code: Select all
#include <stdio.h>
int main()
{
char* example = "Hello my friend!\n";
printf("%s\n", example);
char* example2 = example + 6;
printf("%s\n", example2);
return 0;
}
Using pointers
We can specify a pointer to anything that resides in main memory, namely any data or even code. To do so, we use the * modifier, just like we used it in char*. In the following code, I will use a pointer to int data type, and also show other modifiers to work with pointers.
- Code: Select all
#include <stdio.h>
int main()
{
int *a; // Pointer to int
int b = 2251;
a = &b; // Assigning to a the address of b
printf("%d\n", *a); // Printing the value pointed by a
return 0;
}
The things we said for character strings apply to other pointers as well. In the previous example, we declared int *a. This means we declared the pointer but not the space to store the data it points to. This is a very important concept to keep in mind. And I insist again: declaring pointers doesn't make up space for the data the pointer is pointing to. Please keep this in mind at all times while programming in C.
Ok, but how do we make space (a.k.a. reserve memory) for our variables? Here comes malloc() and friends. malloc() stands for memory allocation. This system call is standard and allows us to reserve memory for our use. Since this is dynamic memory allocation (done during runtime and not during compile-time) we must also take care of freeing this memory using free(). Here's an example:
- Code: Select all
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *a = (int*) malloc(sizeof(int)); // Allocating sizeof(int) bytes of space
int b = 2251;
*a = b; // Copying content of b to a
printf("%d\n", *a);
free(a); // Free a's reserved memory
return 0;
}
Another important thing about pointers is what I call intelligent math: the compiler knows what data type you're pointing to due to the pointer declaration (char* points to a char, int* points to an int, etc...), so it makes sure that when you do math operations with the pointer, the compiler does so taking into account the size of the data type pointed to. This is more clear with an example:
- Code: Select all
#include <stdio.h>
int main()
{
// We have a string
char* string = "This is a pointer use example";
printf("String at memory address: 0x%08X -> '%s'\n", (unsigned int)string, string);
// Now let's have 2 different pointer types point to it as well
char* p1 = string;
int* p2 = (int*) string;
// We increment a char pointer -> incremented by sizeof(char) = 1 byte
p1++;
printf("p1 = 0x%08X, points at: '%s'\n", (unsigned int)p1, p1);
// We increment an int pointer -> incremented by sizeof(int) = 4 bytes (x86)
p2++;
printf("p2 = 0x%08X, points at: '%s'\n", (unsigned int)p2, (char*)p2);
return 0;
}
As you can see, incrementing p1 is not the same as incrementing p2 because they point to data types with different sizes. This is a source of quite hard to track bugs if you don't pay attention.
Array decay convention
In C, arrays can "decay" into pointers and viceversa, which means that arrays are equivalent to pointers. Thus for example char[] is the equivalent of char*. Example:
- Code: Select all
#include <stdio.h>
int main()
{
char *string = "This is a pointer use example";
printf("%s\n", string);
printf("%c, %c\n", string[1], string[5]); // Print second and sixth character
return 0;
}
And finally here are some exercises to practice this stuff
- Using only pointers: declare an array of signed integers and assing it a size of 50 integers.
- Same as the last exercise but also assign positions 5, 6, and 8, and then print them.
- Using only pointers: given a random string, print from the 5th character onwards.
- Given 2 strings of length 5, encrypt the first one using the second one using an XOR operation.
EDIT: I removed some exercises and put new ones since we haven't seen functions yet
EDIT2: changed the exercises since we haven't seen flow control yet
<< Prev Next >>


