- Introduction to C
- Introduction to C
- Program Structure
- Basic Syntax
- Data Types
- Storage Classes
- If statements
- Case control statements
- File I/O
- Type Casting
- Command line arguments
- Header Files
- Dynamic memory allocation
- Error Handling
What are pointers?
Pointers are aptly name: they "point" to a locations in the memory.For example, think of a row of safety deposit boxes at a local bank of various sizes. Each safety deposit box will have a number associated with it so you can quickly look it up. These numbers are similar to the memory addresses of variables. Pointer in the world of safety deposit box would be anything that stored the number of another safety deposit box. Perhaps you have a rich uncle who store valuable things in his safety deposit box, but he decided to put the real location of that safety deposit box in another, smaller, safety deposit box that only stored a card with the number (location) of the large box with the real jewellery or valuable things. The safety deposit box with the card would be storing the location of another box; it would be equivalent to a pointer. In the computer, pointers are variables that store memory addresses, usually they are the addresses of other variables.
The cool thing is that once you can talk about the address of a variable, you will then be able to go to that address and retrieve the data stored in it. If you have a huge piece of data that you want to pass into any function, it's a lot easier to pass its location to the function than to copy each and every element of the data, And if you need more memory for program, you can request more memory from the system--And how do you get "back" that memory? System tells you where it is located; in easy words, you get a memory address back. You need pointers to store the memory address.
A note about terms: the word pointer can refer either to a variable that stores a memory address, or to a memory address itself. The distinction isn't really that important: if you pass a pointer variable into a function, that means you are passing the value stored in the pointer. when any variable that stores a memory address, I will call it a pointer. And when a variable stores the address of any another variable, I will say that it is "pointing to" that variable.
C Pointer Syntax
Pointers require a new syntax because when you have a pointer variable, you need the ability to request both the value and the memory location of that value. Since pointers are special kind of variable, you need to tell the compiler when you declare your pointer variable that the variable is a pointer type variable, and you also have to tell the compiler what type of memory it points to.
The pointer syntax looks like this:
Notice that the use of the * is the key to declaring a pointer; if you add it directly before the variable name, it will declare the variable as a pointer. And if you declare multiple pointers on the same line, you have to precede each of them with an asterisk(*):
There are two ways to use the pointer to access information as I mentioned: it is possible to have it give the address to another variable. Simply use the name of the pointer without the *. However, to access the actual memory location and to get the value stored there use the *(asterisk). The technical name of getting the value of pointer is dereferencing the pointer. It can be tricky to keep track of when you should add the asterisk sign. Remember that the pointer's natural use is to store a memory address; so when you use the pointer:
then it evaluates to the address. You have to add the asterisk, in order to retrieve the value stored at the address. You will probably do that an awful lot. The pointer itself is supposed to store an address, so you get that address back when you use the bare pointer.
Pointing to Something: Retrieving Address
In order to have a pointer which point to another variable it is necessary to have the memory address of that variable also. To get the memory address of any variable ( i.e. location in memory), put the &(ampersand) sign in front of the variable name. It give its address. This is also called address-of operator, because it returns the memory address of any variable. Conveniently, both words ampersand and address-of start with a; It is a useful way to remember that you use & to get the address of a variable.
int z; /* A normal integer*/
int *p; /* A pointer to an integer ("*p" is an integer, so p
must be a pointer to an integer) */
p = &z; /* Read it, "assign the address of z to p" */
scanf( "%d", &z ); /* Put a value in z, we could also use p here */
printf( "%d\n", *p ); /* Note the use of the * to get the value */
The printf outputs the value stored in z. Why is that? Well, let's look at the code. The integer is called z. A pointer to an integer is then defined as p. Then it stores the memory location of z in pointer by using the address operator (&) to get the address of the variable. Using the ampersand(&) is a bit like looking at the label on the safety deposit box to see its number rather than looking inside the box what is in it, to get what it stores. User then inputs a number that is stored in the variable z; remember, this is the same location that is pointed by p. In fact, since we use an ampersand(&) to pass the value of variable to scanf, it should be clear that scanf is putting the value in the address pointed to by p. (We can say that, scanf works because of pointers)
The next line then passes *p into printf. *p performs the "dereferencing" operation on p; it looks at the address stored in p pointer, and goes to that address and returns the value stored. This is same as looking inside a safety deposit box only to find the number of another box, which you then open.
Notice that in the given example, the pointer is initialized to point to a specific memory address before it is used. You can use it for pointing to anything. And this can lead to extremely unpleasant effect to the program. For example, the operating system will probably prevent you from accessing memory that it knows your program doesn't own: this will cause your program to crash. For example, if we had a document opened in Ms Word, we could change the text! Luckily, Windows and other modern operating systems will stop us from accessing that memory and cause our program to crash. To avoid crashing of our program, we should always initialize pointers before we use them.
It is also possible to initialize pointers using free memory. This is called dynamic allocation of memory. It is useful for setting up structures like linked lists or data trees where you don't know memory requirements at compile time, so you have to get memory at the time of program's execution(or run time). We will look at these structures later, but for now, we will simply examine how to request memory from and how to return memory to the operating system.
The function malloc, residing in the stdlib.h header file, malloc works just like any other function call. And it is used to initialize pointers memory from free store (a section of memory available to all programs). The argument to malloc is the amount of memory requested (given in bytes), and malloc gets a block of memory of that size and then returns a pointer to the block of memory allocated.
Since different type of variables have different memory requirements, so we need to get a size for the amount of memory malloc should return. We need to know how to get the size of different type variables. It can be done using the keyword/function sizeof, which takes an variable as expression and returns its size. For example, sizeof(float) would return the number of bytes required to store an float number.
int *ptr = malloc( sizeof(int) );
This code set pointer named ptr to point to a memory address of size int. The memory that is pointed by ptr becomes unavailable to other programs. This means that a careful coder should free this memory at the end of its usage lest the memory be lost to the operating system for the duration of the program (often called a memory leak because the program is not keeping track of all of its memory).
Note: It is slightly cleaner to write malloc statements by taking the size of the variable pointed to by using the pointer directly:
In above code sizeof(*ptr) will evaluate the size of whatever we would get back from dereferencing pointer ptr; since ptr is a pointer to an int, *ptr would give us an int, so function sizeof(*ptr) will return the size of an integer. So why do this? Well, if we rewrite the declaration of ptr the following, then we would have to rewrite only the first part of it:
We don't have to go back and correct the malloc call to use sizeof(float). So far as ptr would be pointing to a float, *ptr would be a float, so sizeof(*ptr) would still give the right size!
This becomes even very useful when you end up allocating memory for a variable far after the point you declare it:
/* hundreds of lines of code */
ptr = malloc( sizeof(*ptr) );
The free function free the memory or returns memory to the operating system.
After freeing a memory, it is a good idea to reset the pointer to point to 0. When 0 is assigned to a pointer variable, the pointer becomes a null pointer, in other words - it points to nothing. When you do something foolish with the pointer (it happens a lot, with experienced programmers also), you find out immediately instead of later, when you have done enough damage
The concept of the null pointer is repeatedly used as a way of indicating a problem--malloc returns 0 when it cannot correctly allocate memory. You have to be sure to handle this correctly--sometimes your operating system might actually run out of memory and give you this value.