A pointer is a variable that represents the location of a data item, such as a variable or an array element. Pointers are used frequently in C, as they have a number of useful applications. For example, pointers can be used to pass information back and forth between a function and its reference point. Pointers provide a way to return multiple data items from a function via function arguments to be specified as arguments to a given function.
Within the computer’s memory, every stored data item occupies one or more adjacent memory cells. The number of memory cells required to store a data item depends on the type of data item. For example, a single character will be stored in 1 byte of memory integer usually requires two adjacent bytes, a floating point number may require four adjacent bytes.
Table of Contents
- How to Declare a Pointer in C?
- How Pointer work?
- Initializing Pointers
- The & and * Operators
- Representation of a Variable
- What is Dynamic Memory Allocation?
- Allocating Multiple Blocks of Memory
- Pointers vs. Arrays
- Pointers and Functions
How to Declare a Pointer in C?
A pointer is declared just like we declare a variable. There is only one difference in declaration is that we add an asterisk * in front of it to indicate that it’s a variable which is a pointer. For example
int* i; long* y;
you can also declare the pointers as
int *i; long *y;
How Pointer work?
Lets write an example to demonstrate the use of pointers.
int number = 10;
int *pnum;
pnum = &number;
Now pnum will be having the address of the number variable.
Initializing Pointers
It is often a good practice to initialize the variable while declaring. It is very easy to initialize a pointer to the address of a variable that has already
been defined.
int num=10;
int* pnum= #
You can also initialize the pointer to the default null value.
int* pnum = NULL;
Here is a simple example that demonstrates the different aspects of the pointer operations.
#include <stdio.h>
int main(void){
int num=10;
int* pnum=NULL;
pnum = #
*pnum += 20;
printf("\nNumber = %d", num);
printf("\nPointer Number = %d", *pnum);
return 0;
}
The & and * Operators
Suppose V is a variable that represents some particular data item. The compiler will automatically assign memory cells for this data item. The data item can be accessed if we know the location of the first memory cell. The address of V’s memory location can be determined by the expression &V, where & is a unary operator, called the address operator, that evaluates the address of its operand.
Now let us assign the address of V to another variable, PV. Thus, PV = &V
This new variable is called a pointer to V, since it “Points” to the location where V is stored in memory. Remember, however, that PV represents V?s address, not its value. Thus, Pv is called pointer variable.
Relationship between PV and V (where PV = &V and V = *PV)
The data item represented by V can be accessed by the expression *PV where * is a unary operator, that operates only on a pointer variable. Therefore, PV and V both represent the same data item. Furthermore, if we write PV = &V and U = PV, then u and v will both represent the same values i.e., the value of V will indirectly be assigned to u.
Example :
int quantity = 179 ;
The statement instructs the system to find a location for the integer quantity and puts the value 179 in that location. Let us re-assume that the system has chosen the address location 5000 for quantity.
Quantity Variable 179 Value 5000 Address
Representation of a Variable
Remember, since a pointer is a variable, its value is also stored in the memory in another location.
The address of P can be assumed to be 5048.
Variable Value Address Quantity 179 5000 P 5000 5048
What is Dynamic Memory Allocation?
C programming requires that the number of elements in an array should be specified at compile time. Many languages permit a programmer to specify an array’s size at run time. The process of allocating memory at run time is known as dynamic memory allocation. The library functions used for allocating memory are :
- malloc() – Allocates requested size of bytes and returns a pointer to the first byte of the allocated space
- calloc() – Allocates space for an array of element, initializes them to zero and then returns a pointer to the memory
Memory Allocation Process
Let us first look at the memory allocation process associated with a C program. Fig. below shows the conceptual view of storage of a C program in memory.
- Local Variable Stack
- Free Memory Heap
- Global Variables
- C Program instructions
The program instructions and global and static variables are stored in a region known as permanent storage area and the local variables are stored in another area called stack. The memory space that is located between these two regions is available for dynamic allocation during execution of the program. The free memory region is called the heap. The size of the heap keeps changing when program is executed due to creation and death of variables that are local to functions and blocks. Therefore, it is possible to encounter memory “overflow” during dynamic allocation process. In such situations, the memory allocations functions mentioned above returns a NULL pointer.
Allocating a Block of Memory
In C, malloc() function is used to allocate block of memory. The malloc() function reserves a block of memory of specified size and returns a pointer of type void. This means that we can assign it to any type of pointer. It takes the following form;
ptr = ( Cast type * ) malloc ( byte size ) ;
Ptr is a pointer of type cast type. The malloc returns a pointer (of cast type) to an area of memory with size byte – size. Example :
x = ( int * ) malloc ( 100 * size of ( int )) ;
On successful execution of this statement, a memory space equivalent to “100 times the size of an int” bytes is reserved and the address of the first byte of the memory allocated is assigned to the pointer X of type int. Similarly, the statement cptr = ( char * ) malloc ( 10 );
allocates 10 bytes of space for the pointer cptr of type char.
Remember, the malloc() allocates a block of adjacent bytes. The allocation can fail if the space in the heap is not sufficient to satisfy the request. If it foils, it returns a NULL. We should therefore check whether the allocation is successful before using the memory pointer.
C Program to Allocate Memory
Write a program that uses a list of integers whose size will be specified interactively at run time.
#include <stdio.h>
#include <stdlib.h>
int main ()
{
int *P, *table ;
int size=5;
printf("In What is the size of Array?\n") ;
scanf("%d", &size ) ;
table = (int*) malloc(size * sizeof(int));
if ( table == NULL )
{
printf ("No space available \n") ;
exit (0) ;
}
printf ("The address of the first byte is %d\n", *table );
printf("\nInput table values: \n");
for (P = table; P < table+size; P++){
scanf ("%d", P );
}
for (P = table + size-1; P >= table; P-- ){
printf ("Value '%d' is stored at address %p\n", *P, &P );
}
return 0;
}
Output of the program
In What is the size of Array? 3 The address of the first byte is 0 Input table values: 2 3 4 Value '4' is stored at address 0x7ffffa52a380 Value '3' is stored at address 0x7ffffa52a380 Value '2' is stored at address 0x7ffffa52a380
Allocating Multiple Blocks of Memory
The calloc() function is another memory allocation function that is normally used for requesting memory space at run time for storing derived data types such as arrays and structures. While malloc() allocates a single block of storage space, calloc() allocates multiple blocks of storage, each of the same size, and then allocates all bytes to O. The general form of calloc() function is:
Pointer = (cast type * ) calloc ( n, elem-size );
The above statement allocates contiguous space for n blocks, each of size elem-size bytes. All bytes are initialized to zero and a pointer to the first byte of the allocated region is returned. If there is not enough space, a NULL pointer is returned.
Pointers vs. Arrays
When you declare an array in C, the compiler allocates a base address and sufficient amount of storage to contain all the elements of the array in contiguous memory locations. The base address is the location of the first element (index 0) of the array. The compiler also defines the array name as a constant pointer to the first element suppose we declare an array X as follows:
Static int X [ 6 ] = { 1, 2, 3, 4, 5, 6 } ;
Suppose the base address of X is 1000 and assuming that each integer requires two bytes, the five elements will be stored as follows:
ELEMENTS x[0] x[1] x[2] x[3] x[4] x[5]
VALUE 1 2 3 4 5 6
Address 1000 1002 1004 1006 1008 1010
BASE ADDRESS
The name X is defined as a constant pointer pointing to the first clement, x [0] and therefore the value of X is 1000, the location whose X[0] is stored.
X = & x[0] = 1000
If we declare P as an integer pointer, then we can make the pointer P to point to the array X by
the following assignment:
P = X ;
This is equivalent to P = &X[0] ;
Now we can access every value of x using P++ to more from one element to another. The relationship between P and X is shown below:
P = &x[0] ( = 1000)
P+1 = &x[1] ( = 1002)
P+2 = &x[2] ( = 1004)
P+3 = &x[3] ( = 1006)
P+4 = &x[4] ( = 1008)
P+5 = &x[5] ( = 1010)
The address of an element is calculated using its index and the scale factor of the data type. For instance,
address of X[3] = base address + (3 X Scale factor of int) = 1000 + (3 x 2) = 1006
When handling array, instead of using array indexing, we can use pointers to access array elements. Note that X(P+3) gives the value of X[3]. The pointer accessing method is more faster than array indexing.
Pointers and Functions
When an array is passed to a function as an argument, only the address of the first element of the array is passed, but not the actual values of the array elements. The function uses this address for manipulating the array elements. Similarly, we can pass the address of a variable as an argument to a function in the normal fashion. When we pass addresses to a function, the parameters receiving the addresses should be pointers. The process of calling function using pointers to pass the address of variable is known as call by reference. The function which is called by reference can change the value of the variable used in the call.
Program to Demonstrate Call by Reference
#include <stdio.h>
int main () {
int X;
X = 40;
change (&X);
printf (" %d", X);
return 0;
}
void change (int *P)
{
*P = *P + 10 ;
}
When the function change() is called, the address of the variable X is passed into the function change (). Inside change(), the variable P is declared as a pointer and therefore P is the address of the variable X. The following statement
*P = *P + 10;
means add 10 to the value stored at address P. Since P represents the address of X, the value of X is changed from 40. Therefore, the output of the program will be 50 not 40.
These, call by reference provides a mechanism by which the function can change the stored values in the calling function.
Pointers are more powerful when used as double-pointer in C. They allow efficient memory allocation and used to write efficient programs. Pointers in C++ works similarly. However, C++ has more support that uses pointers for more use cases. Such use cases are referencing virtual functions inside a class, smart pointers, use of the new and delete keywords etc.