Unit III
Unit III
3.2 POINTERS
3.2.1 Introduction to Pointers
The pointer in C language is a variable which stores the address of another
variable. This variable can be of type int, char, array, function, or any other
pointer.
Declaring a pointer
The pointer in c language can be declared using * (asterisk symbol). It is
also known as indirection pointer used to dereference a pointer.
int *a;//pointer to int
char *c;//pointer to char
Defining a pointer
int n = 50;
int* p = &n; // Variable p of type pointer is pointing to the address of
the variable n of type integer.
Pointer Example
An example of using pointers to print the address and value is given below.
As you can see in the above figure, pointer variable stores the address of
number variable, i.e., fff4. The value of number variable is 50. But the
address of pointer variable p is aaa3.
By the help of * (indirection operator), we can print the value of pointer
variable p.
Example:
#include<stdio.h>
void main(){
int number=50;
int *p;
p=&number;//stores the address of number variable
printf("Address of number variable is %x \n",p);
printf("Value of p variable is %d \n",*p);
}
Output
Address of number variable is fff4
Value of p variable is 50
3.2.2 Pointer expression and Pointer arithmetic
Like other variables, pointer variables can also be used in expressions. For
example, if ptr1 and ptr2 are pointers, then the following statements are
valid.
int num1=2, num2= 3, sum=0, mul=0, div=1;
int *ptrl, *ptr2;
ptrl = &num1;
ptr2 = &num2;
sum = *ptrl + *ptr2;
mul = sum* *ptrl;
*ptr2 +=1;
div = 9 + *ptr1/*ptr2 - 30;
C also allows to compare pointers by using relational operators in the
expressions. For example, p1 > p2, p1 == p2, and p1! = p2 are all valid in
C.
When using pointers, unary increment (++) and decrement (--) operators
have greater precedence than the dereference operator (*). Both these
operators have a special behaviour when used as suffix. In that case the
expression is evaluated with the value it had before being increased.
Therefore, the expression
*ptr++
is equivalent to * (ptr++) as ++ has greater operator precedence than *.
Therefore, the expression will increase the value of ptr so that it now points
to the next memory location. This means the statement *ptr++ does not
perform the intended task. Therefore, to increment the value of the variable
whose address is stored in ptr, you should write
(*ptr) ++
Array notation is a form of pointer notation. The name of the array is the
starting address of the array in memory. It is also known as the base address.
In other words, base address is the address of the first element in the array
or the address of arr [0] . Now let us use a pointer variable as given in the
statement below.
int *ptr;
ptr = &arr [0];
Here, ptr is made to point to the first element of the array.
If ptr originally points to arr [2], then ptr++ will point to the next element,
i.e., arr [3].
Example:
#include <stdio.h>
void main()
{
int arr[]={1,2,3,4,5};
printf("\n Address of array = %p %p %p", arr, &arr[0], &arr);
}
Output
Address of array = 0x7ffd36139bf0 0x7ffd36139bf0 0x7ffd36139bf0
3.2.5 Passing an Array to Functions
An array can be passed to a function using pointers. For this, a function that
expects an array can declare the formal parameter in either of the two
following ways:
func (int arr[]); OR func (int *arr);
Example
#include <stdio.h>
#include <conio.h>
void read_array (int *arr, int n);
void print_array(int *arr, int n);
void find_small (int *arr, int n, int *small, int *pos);
void main(){
int num[10], n, small, pos;
printf("\n Enter the size of the array: ");
scanf("%d", &n);
read_array (num, n);
print_array (num, n);
find_small (num, n, &small, &pos);
printf("\n The smallest number in the array is %d at position %d", small,
pos);
getch();
}
void read_array(int *arr, int n)
{
int i;
printf("\n Enter the array elements: ");
for (i=0; i<n;i++)
scanf("%d", &arr[i]);
}
void print_array (int *arr, int n)
{
int i;
printf("\n The array elements are: ");
for (i=0;i<n;i++)
printf("\t %d", arr[i]);
}
void find_small (int *arr, int n, int *small, int *pos)
{
int i;
for (i=0;i<n;i++){
if (* (arr+i) < *small){
* small = * (arr+i);
*pos = i;
}
}
}
Output
Enter the size of the array: 5
Enter the array elements: 1 2 3 4 5
The array elements are: 1 2 3 4 5
The smallest number in the array is 1 at position 0
3.2.6 Function Pointers
In order to declare a pointer to a function we have to declare it like the
prototype of the function except that the name of the function is enclosed
between parentheses () and an asterisk (*) is inserted before the name. The
syntax of declaring a function pointer can be given as
return_type (*function_pointer_name) (argument_list);
Initializing a Function Pointer
As in case of other pointer variables, a function pointer must be initialized
prior to use. If we have declared a pointer to the function, then that pointer
can be assigned the address of the correct function just by using its name.
Like in the case of an array, a function name is changed into an address
when it's used in an expression. It is optional to use the address operator
(&) in front of the function name.
For example, if fp is a function pointer and we have a function add () with
prototype given as
int add (int, int);
Then writing fp = add; initializes the function pointer fp with the address of
add ().
Calling a Function Using a Function Pointer
When a pointer to a function is declared, it can be called using one of two
forms:
(*func) (1,2); OR func (1,2);
Example:
#include<stdio.h>
#include<conio.h>
void print(int n);
void *fp(int);
void main(){
fp = print;
(*fp) (10);
fp (20);
getch();
}
void print(int value){
printf("\n %d", value);
}
Output
10
20
Comparing Function Pointers
Comparison operators such as = = and != can be used the same way as usual.
Consider the code given below which checks if fp actually contains the
address of the function print (int).
if (fp >0) // check if initialized
{
if (fp == print)
printf("\n Pointer points to print ");
else
printf("\n Pointer not initialized!");
}
Passing a Function Pointer as an Argument to a Function
A function pointer can be passed as the calling argument of a function. This
is in fact necessary if you want to pass a pointer to a callback function. The
following code shows how to pass a pointer to a function which returns an
int and accepts two int values.
Note that in the program below, the function operate calls the functions add
and subtract with the following line:
result = (*operate_fp) (num1,num2);
#include <stdio.h>
int add (int, int);
int sub (int, int);
int operate (int (*operate_fp) (int, int), int, int);
void main()
{
int result;
result = operate (add, 9, 7);
printf ("\n Addition result = %d", result); n
result = operate (sub, 9, 7);
printf ("\n Subtraction = %d", result);
}
int add (int a, int b)
{
return (a + b);
}
int sub (int a, int b)
{
return (a - b);
}
int operate (int (*operate_fp) (int, int), int a, int b)
{
int result;
result = (*operate_fp) (a, b);
return result;
}
Output
Addition = 16
Subtraction = 2
3.2.7 Pointers to Pointers
In C language you are also allowed to use pointers that point to pointers.
The pointers in turn, point to data (or even to other pointers). To declare
pointers to pointers, just add an asterisk (*) for each level of reference. For
example, if we have:
int x = 10
int *px; //pointer to an integer
int **ppx; // pointer to a pointer to an integer
px = &x;
ppx = &px;
Assume that the memory location of these variables is as shown in
Figure
Now if we write,
printf("\n %d", **ppx);
then it will print 10, the value of x.
3.2.8 Drawbacks of pointers
Errors: Pointers can cause errors such as segmentation errors,
unrequired memory access, and memory corruption.
Memory leaks: Pointers can lead to memory leaks.
Difficulty to understand: Pointers can be difficult to understand.
Slower than variables: Pointers are generally slower than variables.
Dangling pointers: Dangling pointers can create errors that are
difficult to diagnose.
Undefined behavior: Undefined behavior can occur when two pointers
point to the same memory location and one of the pointers is freed.
Garbage values: Garbage values in memory are values that cannot be
accessed.
Pointer arithmetic: Pointer arithmetic can cause security flaws. For
example, if a pointer is adjusted across the bounds of an object, the
system cannot detect the error.
Null pointer dereferences: Excessive pointer usage can lead to null
pointer dereferences. These usually result in the process failing, unless
exception handling is available.
Wild pointers: A wild pointer is a pointer that has not been initialized
to a memory location before it is used. Using a wild pointer can lead
to unexpected behavior or data corruption.