Students Material For Pointers Amd Memory Allocation
Students Material For Pointers Amd Memory Allocation
1 INTRODUCTION TO POINTERS
Pointers in C are easy and fun to learn. Some C programming tasks are performed more easily with pointers, and other
tasks, such as dynamic memory allocation, cannot be performed without using pointers. So it becomes necessary to learn
pointers to become a perfect C programmer. Let's start learning them in simple and easy steps.
A pointer is a variable whose value is the address of another variable, i.e., direct address
of the memory location. Like any variable or constant, you must declare a pointer
before using it to store any variable address. The general form of a pointer variable
declaration is −
type *var-name;
Here, type is the pointer's base type; it must be a valid C data type and var-name is the name of the pointer
variable. The asterisk * used to declare a pointer is the same asterisk used for
multiplication. However, in this statement the asterisk is being used to designate a
variable as a pointer. Take a look at some of the valid pointer declarations −
The actual data type of the value of all pointers, whether integer, float, character, or otherwise, is the same, a long
hexadecimal number that represents a memory address. The only difference between pointers of different data types is the
data type of the variable or constant that the pointer points to.
As you know, every variable is a memory location and every memory location has its address defined which can be
accessed using ampersand (&) operator, which denotes an address in memory.
If you have a variable var in your program, &var will give you its address in the memory, where & is commonly called
the reference operator.
You must have seen this notation while using scanf() function. It was used in the function to store the user inputted value
in the address of var.
scanf("%d", &var);
Example 7.1
int main()
int var = 5;
return 0;
Output
Value: 5
Address: 2686778
In C, there is a special variable that stores just the address of another variable. It is called Pointer variable or, simply, a
pointer.
data_type* pointer_variable_name;
For example,
int* p;
As discussed, & is called reference operator. It gives you the address of a variable.
Likewise, there is another operator that gets you the value from the address, it is called a dereference operator (*).
There are a few important operations, which we will do with the help of pointers very frequently.
(a) We define a pointer variable,
(b) assign the address of a variable to a pointer and
(c) finally access the value at the address available in the pointer variable.
Below example clearly demonstrates the use of pointers, reference operator and dereference operator.
Note: The * sign when declaring a pointer is not a dereference operator. It is just a similar notation that creates a pointer.
Example 7.2 :
#include <stdio.h>
int main()
int* pc;
int c;
c=22;
printf("Address of c:%u\n",&c);
printf("Value of c:%d\n\n",c);
pc=&c;
c=11;
*pc=2;
printf("Address of c:%u\n",&c);
printf("Value of c:%d\n\n",c);
return 0;
}
Output
Address of c: 2686784
Value of c: 22
Address of c: 2686784
Value of c: 2
It is always a good practice to assign a NULL value to a pointer variable in case you do not have an exact address to be
assigned. This is done at the time of variable declaration. A pointer that is assigned NULL is called a null pointer.
The NULL pointer is a constant with a value of zero defined in several standard
libraries. Consider the following program −
#include <stdio.h>
int main ()
{
int *ptr = NULL;
printf("The value of ptr is : %x\n", ptr );
return 0;
}
Output:
When the above code is compiled and executed, it produces the following result −
To check for a null pointer, you can use an 'if' statement as follows –
A pointer to a pointer is a form of multiple indirection, or a chain of pointers. Normally, a pointer contains the address of
a variable. When we define a pointer to a pointer, the first pointer contains the address of the second pointer, which points
to the location that contains the actual value as shown below.
66X123X1
XX771230 123
XX661111
66X123X1 XX771230
A variable that is a pointer to a pointer must be declared as such. This is done by placing an additional asterisk in front of
its name. For example, the following declaration declares a pointer to a pointer of type int –
int **var;
int main()
{
int num=123;
pr2 = & num; // the address of variable num is stored in pointer pr2
pr1 = &pr2; // storing the address of pointer pr2 into another pointer pr1
return 0;
}
Output:
Below points will help out to clarify few things in a better way –
&pr2 = pr1
A pointer in c is an address, which is a numeric value. Therefore, you can perform arithmetic operations on a pointer just
as you can on a numeric value. There are four arithmetic operators that can be used on pointers: ++, --, +, and -
To understand pointer arithmetic, let us consider that ptr is an integer pointer which points to the address 1000. Assuming
32-bit integers, let us perform the following arithmetic operation on the pointer:
ptr++
After the above operation, the ptr will point to the location 1004 because each time ptr is incremented, it will point to the
next integer location which is 4 bytes next to the current location. This operation will move the pointer to the next
memory location without impacting the actual value at the memory location. If ptr points to a character whose address is
1000, then the above operation will point to the location 1001 because the next character will be available at 1001.
The same considerations apply to decrementing a pointer, which decreases its value by the number of bytes of its data
type. Thus, the following operations can be performed on a pointer:
main( )
{
int arr[ ] = { 10, 20, 36, 72, 45, 36 } ;
int *j, *k ;
j = &arr [ 4 ] ;
k = ( arr + 4 ) ;
if ( j == k )
printf ( "The two pointers point to the same location" ) ;
else
printf ( "The two pointers do not point to the same location" ) ;
}
7.4 POINTERS AND ARRAYS
Let us consider a block in memory consisting if ten integers in a row. That is, 20 bytes of memory are set aside to hold 10
integers. Now, let's say we point our integer pointer ptr at the first of these integers. Furthermore lets say that integer is
located at memory location 100 (decimal). What happens when we write: ptr + 1; Because the compiler "knows" this is a
pointer (i.e. its value is an address) and that it points to an integer (its current address, 100, is the address of an integer), it
adds 2 to ptr instead of 1, so the pointer "points to" the next integer, at memory location 102.
We prefer using a pointer in our program instead of an array because the variable pointer can be incremented, unlike the
array name which cannot be incremented because it is a constant pointer. When performing addition on a pointer, size of a
pointer increments by no. of storage units and not by no. of bytes. This is the beauty of scaling that pointer arithmetic
doesn’t depend on type pointer is pointing to. Meaning that if a pointer points to character, when incremented by 1, points
to next character, or if it points to float, points to next float when incremented by 1 and so on.
Example 7.4 : The following program increments the variable pointer to access each succeeding element of the
array –
#include <stdio.h>
int main ()
int i, *ptr;
ptr = var;
ptr++;
}
return 0;
Output:
Value of var[0] = 10
Accessing array elements by pointers is always faster than accessing them by subscripts. However, from the point of view
of convenience in programming we should observe the following:
Array elements should be accessed using pointers if the elements are to be accessed in a fixed order, say from beginning
to end, or from end to beginning, or every alternate element or any such
definite logic. Instead, it would be easier to access the elements using a subscript if there is no fixed logic in accessing the
elements. However, in this case also, accessing the elements by pointers would work faster than subscripts.
There may be a situation when we want to maintain an array, which can store pointers to an int or char or any other data
type available. Following is the declaration of an array of pointers to an integer
int *ptr[MAX];
It declares ptr as an array of MAX integer pointers. Thus, each element in ptr, holds a pointer to an int value. The
following example uses three integers, which are stored in an array of pointers, as follows
Example 7.5.1
#include <stdio.h>
#include <stdio.h>
const int MAX = 3;
int main () {
int i, *ptr[MAX];
return 0;
Example 7.5.2
Write a program in which two-dimensional array is represented as an array of integer pointers to a set of single-dimension
integer arrays
# include <stdio.h>
#include <stdlib.h>
#define MAXROWS 3
void main()
for (i=0;i<rows;i++)
int i, j;
return;
int i, j;
printf(“%d”, *(*ptr3+i)+j));
printf(“\n”);
return;
int i, j;
return;
OUTPUT
3 3
5 4 3
7 8 9
3 2 1
2 4 1
4 6 5
1 1 0
3 0 2
3 2 4
2 1 1
In this program, ptr1, ptr2, ptr3 are each defined as an array of pointers to integers. Each array has a maximum of
MAXROWS elements. Since each element of ptr1, ptr2, ptr3 is a pointer, we must provide each pointer with enough
memory for each row of integers. This can be done using the library function malloc included in stdlib.h header file as
follows:
ptr1[1] = (int*)malloc(cols*sizeof(int));
This function reserves a block of memory whose size(in bytes) is equivalent to cols * sizeof(int). Since cols=3, so 3*2(size
of int data type)i.e., 6 is allocated to each ptr1[1], ptr1[2] and ptr1[3]. This malloc function returns a pointer of type void.
This means that we can assign it to any type of pointer. In this case, the pointer is type-casted to an integer type and
assigned to the pointer ptr1[1], ptr1[2] and ptr1[3] points to the first byte of the memory allocated to the corresponding set
of one-dimensional integer arrays of the original two-dimensional array.
The process of calculating and allocating memory at run time is known as dynamic memory allocation. The library routine
malloc can be used for this purpose.
Instead of using conventional array notation, pointer notation has been used for accessing the address and value of
corresponding array elements.the difference of the array elements within the function calcdiff is written as
*(*(ptr3 + i) +j) = *(*(ptr1 + i) +j) - *(*(ptr2 + i) +j)
Suppose we wish to store “Hello”. We may either store it in a string or we may ask the C compiler to store it at some
location in memory and assign the address of the string in a char pointer. This
is shown below:
char *p = "Hello" ;
There is a subtle difference in usage of these two forms. For example, we cannot assign a string to another, whereas, we
can assign a char pointer to another char pointer. This is shown in the
following program.
main( )
char str2[10] ;
char *q ;
q = s ; /* works */
}
Also, once a string has been defined it cannot be initialized to another set of characters. Unlike strings, such an operation
is perfectly valid with char pointers.
main( )
char *p = "Hello" ;
p = "Bye" ; /* works */
Example 7.6.1
#include <stdio.h>
int is_palindrome(char*);
void reverse_string(char*);
int string_length(char*);
int main()
char string[100];
int result;
printf("Input a string\n");
gets(string);
result = is_palindrome(string);
if ( result == 1 )
else
return 0;
char *reverse;
length = string_length(string);
reverse = (char*)malloc(length+1);
copy_string(reverse, string);
reverse_string(reverse);
free(reverse);
if ( check == 0 )
return 1;
else
return 0;
}
int length = 0;
while(*string)
length++;
string++;
return length;
while(*source)
*target = *source;
source++;
target++;
*target = '\0';
int length, c;
char *begin, *end, temp;
length = string_length(string);
begin = string;
end = string;
end++;
temp = *end;
*end = *begin;
*begin = temp;
begin++;
end--;
while(*first==*second)
break;
first++;
second++;
return 0;
else
return -1;
An indirection operator is an operator used to obtain the value of a variable to which a pointer points. While a pointer
pointing to a variable provides an indirect access to the value of the variable stored in its memory address, the indirection
operator dereferences the pointer and returns the value of the variable at that memory location. The indirection operator is
a unary operator represented by the symbol (*).
The indirection operator can be used in a pointer to a pointer to an integer, a single-dimensional array of pointers to
integers, a pointer to a char, and a pointer to an unknown type.
A pointer variable is like an ordinary variable in that it is allocated location, by compiler, in memory when declared in the
program. But unlike ordinary variable, a pointer holds address. Since memory is Byte addressable, every byte has address.
Address itself is a constant value. Pointer holds addresses, meaning that what address does it hold, it points to that
location in memory.
Example 7.7.1:
#include <stdio.h>
int main(void)
int i = 100;
return 0;
Address of i is 0x7fffb6801824
Observe here that both addresses are same. Value of pointer variable, which is address of integer i, points to the location
allocated to integer i. But how to access the value at that location. In simple ways, integer ‘i’ gives value at that location.
Example 7.7.2:
#include <stdio.h>
int main(void)
int i = 100;
return 0;
Address of i is 0x7ffffb7f6ce4
Notice in the last printf() statement, we accessed the value of the integer i indirectly using integer pointer ip. Since we
have indirectly accessed i, we perhaps call this Indirection or Dereferencing the pointer and unary operator (*) the
Indirection operator.
Pointer doesn’t have built-in property of Indirection. Preceding a pointer with unary (*) operator, like *ip; in the exp. *ip
= &i, is read as “go to the location pointed to by pointer, and access the I-value at that location”.
Also note that Indirection operator is one of very few operators which makes pointer expressions as ‘modifiable values’
meaning that such expressions represent locations in memory and can be used on left side of assignment ‘=’ operator.
a = *(*d[5])(x, y)
will place an integer in a, even if you are not sure what happened. You could similarly match types by stripping off
matching levels of indirections:
b = (*d[5])(x, y)
would store an integer pointer in b rather than the value of the integer.
Although the expression given in the declaration is generally the correct way to use the variable, the relation between
pointers and arrays allows for other uses. Since an array name is just a pointer, we can actually use the expressions
b[] and *c
as well. (Using the alternate notation is often confusing but occasionally more clear.) For example, the sixth element
of an array, declared by either of the two methods mentioned above, can be accessed in either of the two following
methods:
b[5] or *(b+5)
(Recall that the monadic "*" operator merely takes the value at the right and performs one level of indirection on it.) The
second method adds 5*(size of the array element type) to the address of array, resulting in a pointer to the sixth element,
and then the "*" causes an indirection on that address, resulting in the value stored there. The subscripted notation is
merely shorthand for this.
C uses two implementations of arrays, depending on the declaration. They are the same for one dimension, but different
for more dimensions.
int array[10][20][30];
than there are exactly 6000 ints of storage allocated, and a reference of the form array[i][j][k]
will be translated to *( array + i*20*30 + j*30 + k ) which calculates the correct offset from the pointer "array", and
then does an indirection on it.
Here each dimension is represented by a vector of pointers of objects of the next dimension, except the last dimension,
which consists of arrays of data.
int ***array;
(and we will assume for the moment that it has been allocated space for a 10*20*30 array), then there is an array of
10 pointers to pointers to ints, 10 arrays of 20 pointers to ints, and 6000 ints. The 200 elements of the 10 arrays each
point to a block of 30 ints, and the 10 elements of the one array each point to one of the 10 arrays. The array variable
points to the head of the array with 10 elements.
In short, "array" points to a pointer to a pointer to an integer, "*array" points to a pointer to an integer, "**array"
points to an integer, and "***array" is an integer.
In this case, an access of the form
array[i][j][k]
*( *( *(var+i) + j ) + k )
Which means: Take a pointer to the main array, add i to offset to the pointer to the correct second dimension array
and indirect to it. Now we have a pointer to one of the arrays of 20 pointers, and we add j to get the offset to the next
dimension, and we do an indirection on that. We now have a pointer to an array of 30 integers, so we add k to get a
pointer to the desired integer, do an indirection, and we have the integer.
In C, like normal data pointers (int *, char *, etc), we can have pointers to functions. Following is a simple example that
shows declaration and function call using function pointer.
Example 7.8.1
#include <stdio.h>
void fun(int a) // A normal function with an int parameter and void return type
int main()
void (*fun_ptr)(int);
fun_ptr = &fun;
*/
(*fun_ptr)(10); // Invoking fun() using fun_ptr
return 0;
Output:
Value of a is 10
Why do we need an extra bracket around function pointers like fun_ptr in above example?
If we remove bracket, then the expression “void (*fun_ptr)(int)” becomes “void *fun_ptr(int)” which is declaration of a
function that returns void pointer
1) Unlike normal pointers, a function pointer points to code, not data. Typically a function pointer stores the start of
executable code.
2) Unlike normal pointers, we do not allocate de-allocate memory using function pointers.
3) A function’s name can also be used to get functions’ address. For example, in the below program, we have removed
address operator ‘&’ in assignment. We have also changed function call by removing *, the program still works.
#include <stdio.h>
void fun(int a)
int main()
return 0;
Output:
Value of a is 10
4) Like normal pointers, we can have an array of function pointers. Below example in point 5 shows syntax for array of
pointers.
5) Function pointer can be used in place of switch case. For example, in below program, user is asked for a choice
between 0 and 2 to do different tasks.
Example 7.8.2
#include <stdio.h>
}
int main()
"for multiply\n");
scanf("%d", &ch);
(*fun_ptr_arr[ch])(a, b);
return 0;
Output:
Multiplication is 150
6) Like normal data pointers, a function pointer can be passed as an argument and can also be returned from a function.
For example, consider the following C program where wrapper() receives a void fun() as parameter and calls the passed
function.
Example 7.8.3
#include <stdio.h>
fun();
int main()
wrapper(fun1);
wrapper(fun2);
return 0;
This point in particular is very useful in C. In C, we can use function pointers to avoid code redundancy. For example a
simple qsort() function can be used to sort arrays in ascending order or descending or by any other order in case of array
of structures. Not only this, with function pointers and void pointers, it is possible to use qsort for any data type.
#include <stdio.h>
#include <stdlib.h>
int main ()
int n = sizeof(arr)/sizeof(arr[0]), i;
return 0;
Output:
5 10 12 15 80 90
The process of allocating memory during program execution is called dynamic memory allocation.
1. malloc()
2. calloc()
3. realloc()
4. free()
Function Syntax
● malloc () function is used to allocate space in memory during the execution of the program.
● malloc () does not initialize the memory allocated during execution. It carries garbage value.
● malloc () function returns null pointer if it couldn’t able to allocate requested amount of memory.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
char *mem_allocation;
else
{
"%s\n", mem_allocation );
free(mem_allocation);
OUTPUT:
● calloc () function is also like malloc () function. But calloc () initializes the allocated memory to zero. But, malloc()
doesn’t.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
char *mem_allocation;
else
"%s\n", mem_allocation );
free(mem_allocation);
OUTPUT:
● realloc () function modifies the allocated memory size by malloc () and calloc () functions to new size.
● If enough space doesn’t exist in memory of current block to extend, new block is allocated for the full size of reallocation,
then copies the existing data to new block and then frees the old block.
● free () function frees the allocated memory by malloc (), calloc (), realloc () functions and returns the memory to the
system.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char *mem_allocation;
else
"%s\n", mem_allocation );
mem_allocation=realloc(mem_allocation,100*sizeof(char));
else
"100 characters");
free(mem_allocation);
OUTPUT:
Dynamically allocated memory content : Problem Solving Using C
Resized memory : space is extended upto 100 characters
malloc() calloc()
int *ptr;ptr = malloc( 20 * sizeof(int) );For int *ptr;Ptr = calloc( 20, 20 * sizeof(int) );For
the above, 20*4 bytes of memory only the above, 20 blocks of memory will be created
allocated in one block. and each contains 20*4 bytes of memory.
Total = 80 bytes Total = 1600 bytes
7.10 Summary
▪ A pointer in c is an address, which is a numeric value. Therefore, you can perform arithmetic operations on a pointer
just as you can on a numeric value.
▪ Pointer variables can be compared provided both variables point to objects of the same data type.
▪ When performing addition on a pointer, size of a pointer increments by no. of storage units and not by no. of bytes.
▪ Accessing array elements by pointers is always faster than accessing them by subscripts.
▪ The process of allocating memory during program execution is called dynamic memory allocation.
▪ malloc () function is used to allocate space in memory during the execution of the program.
▪ calloc () function is also like malloc () function, but calloc () initializes the allocated memory to zero.
▪ realloc () function modifies the allocated memory size by malloc () and calloc () functions to new size.
▪ free () function frees the allocated memory by malloc (), calloc (), realloc () functions and returns the memory to the
system.