UNIT -2: Pointers

Pointers

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 −int *ip; /* pointer to an integer */ double *dp; /* pointer to a double */ float *fp; /* pointer to a float */ char *ch /* pointer to a character */

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.

How to Use Pointers?

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. This is done by using unary operator * that returns the value of the variable located at the address specified by its operand.

NULL Pointers

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; }

When the above code is compiled and executed, it produces the following result −The value of ptr is 0

In most of the operating systems, programs are not permitted to access memory at address 0 because that memory is reserved by the operating system. However, the memory address 0 has special significance; it signals that the pointer is not intended to point to an accessible memory location. But by convention, if a pointer contains the null (zero) value, it is assumed to point to nothing.

To check for a null pointer, you can use an ‘if’ statement as follows −if(ptr) /* succeeds if p is not null */ if(!ptr) /* succeeds if p is null */

Indirection operator

An indirection operator, in the context of C#, 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.

The indirection operator is also known as the dereference operator.

The (*) symbol is used in declaring pointer types and in performing pointer indirection, while the ‘address-of’ operator () returns the address of a variable. Hence, the indirection operator and the address-of operator are inverses of each other.

C# allows using pointers only in an unsafe region, which implies that the safety of the code within that region is not verified by the common language runtime (CLR). In the unsafe region, the indirection operator is allowed to read and write to a pointer. The following C# statements illustrate the usage of the indirection operator:

  • int a = 1, b; // line 1
  • int *pInt = &a; // line 2
  • b = *pInt; // line 3

In the first line above, a and b are integer variables and a is assigned a value of 1. In line 2, the address of a is stored in the integer pointer pInt (line 2). The dereference operator is used in line 3 to assign the value at the address pointed to by pInt to the integer variable b.

The indirection operator should be used to dereference a valid pointer with an address aligned to the type it points to, so as to avoid undefined behavior at runtime. It should not be applied to a void pointer or to an expression that is not of a pointer type, to avoid compiler errors. However, after casting a void pointer to the right pointer type, the indirection operator can be used.

When declaring multiple pointers in a single statement, the indirection operator should be written only once with the underlying type and not repeated for each pointer name. The indirection operator is distributive in C#, unlike C and C++. When the indirection operator is applied to a null pointer, it results in an implementation-defined behavior. Since this operator is used in an unsafe context, the keyword unsafe should be used before it along with the /unsafe option during compilation.

Address operators

The “Address Of” Operator denoted by the ampersand character (&), & is a unary operator, which returns the address of a variable.

After declaration of a pointer variable, we need to initialize the pointer with the valid memory address; to get the memory address of a variable Address Of” (&) Operator is used.

Consider the program #include <stdio.h>. int main() {. int x=10; //integer variable *ptrX; //integer pointer declaration ptrX=&x; //pointer initialization with the address of x printf(“Value of x is %d\n”,*ptrX); return 0; }

OutputValue of x: 10

Pointer Arithmetic in C

We can perform arithmetic operations on the pointers like addition, subtraction, etc. However, as we know that pointer contains the address, the result of an arithmetic operation performed on the pointer will also be a pointer if the other operand is of type integer. In pointer-from-pointer subtraction, the result will be an integer value. Following arithmetic operations are possible on the pointer in C language:

  • Increment
  • Decrement
  • Addition
  • Subtraction
  • Comparison

Incrementing Pointer in C

If we increment a pointer by 1, the pointer will start pointing to the immediate next location. This is somewhat different from the general arithmetic since the value of the pointer will get increased by the size of the data type to which the pointer is pointing.

We can traverse an array by using the increment operation on a pointer which will keep pointing to every element of the array, perform some operation on that, and update itself in a loop.

The Rule to increment the pointer is given below:

  1. new_address= current_address + i * size_of(data type)  

Where i is the number by which the pointer get increased.

32-bit

For 32-bit int variable, it will be incremented by 2 bytes.

64-bit

For 64-bit int variable, it will be incremented by 4 bytes.

Let’s see the example of incrementing pointer variable on 64-bit architecture.

  1. #include<stdio.h>  
  2. int main(){  
  3. int number=50;        
  4. int *p;//pointer to int      
  5. p=&number;//stores the address of number variable        
  6. printf(“Address of p variable is %u \n”,p);        
  7. p=p+1;        
  8. printf(“After increment: Address of p variable is %u \n”,p); // in our case, p will get incremented by 4 bytes.      
  9. return 0;  
  10. }    

Output

Address of p variable is 3214864300 After increment: Address of p variable is 3214864304


Traversing an array by using pointer

  1. #include<stdio.h>  
  2. void main ()  
  3. {  
  4.     int arr[5] = {1, 2, 3, 4, 5};  
  5.     int *p = arr;  
  6.     int i;  
  7.     printf(“printing array elements…\n”);  
  8.     for(i = 0; i< 5; i++)  
  9.     {  
  10.         printf(“%d  “,*(p+i));  
  11.     }  
  12. }  

Output

printing array elements… 1 2 3 4 5


Decrementing Pointer in C

Like increment, we can decrement a pointer variable. If we decrement a pointer, it will start pointing to the previous location. The formula of decrementing the pointer is given below:

  1. new_address= current_address – i * size_of(data type)  

32-bit

For 32-bit int variable, it will be decremented by 2 bytes.

64-bit

For 64-bit int variable, it will be decremented by 4 bytes.

Let’s see the example of decrementing pointer variable on 64-bit OS.

  1. #include <stdio.h>            
  2. void main(){            
  3. int number=50;        
  4. int *p;//pointer to int      
  5. p=&number;//stores the address of number variable        
  6. printf(“Address of p variable is %u \n”,p);        
  7. p=p-1;       
  8. printf(“After decrement: Address of p variable is %u \n”,p); // P will now point to the immidiate previous location.         
  9. }      

Output

Address of p variable is 3214864300 After decrement: Address of p variable is 3214864296


C Pointer Addition

We can add a value to the pointer variable. The formula of adding value to pointer is given below:

  1. new_address= current_address + (number * size_of(data type))  

32-bit

For 32-bit int variable, it will add 2 * number.

64-bit

For 64-bit int variable, it will add 4 * number.

Let’s see the example of adding value to pointer variable on 64-bit architecture.

  1. #include<stdio.h>  
  2. int main(){  
  3. int number=50;        
  4. int *p;//pointer to int      
  5. p=&number;//stores the address of number variable        
  6. printf(“Address of p variable is %u \n”,p);        
  7. p=p+3;   //adding 3 to pointer variable    
  8. printf(“After adding 3: Address of p variable is %u \n”,p);       
  9. return 0;  
  10. }    

Output

Address of p variable is 3214864300 After adding 3: Address of p variable is 3214864312

As you can see, the address of p is 3214864300. But after adding 3 with p variable, it is 3214864312, i.e., 4*3=12 increment. Since we are using 64-bit architecture, it increments 12. But if we were using 32-bit architecture, it was incrementing to 6 only, i.e., 2*3=6. As integer value occupies 2-byte memory in 32-bit OS.


C Pointer Subtraction

Like pointer addition, we can subtract a value from the pointer variable. Subtracting any number from a pointer will give an address. The formula of subtracting value from the pointer variable is given below:

  1. new_address= current_address – (number * size_of(data type))  

32-bit

For 32-bit int variable, it will subtract 2 * number.

64-bit

For 64-bit int variable, it will subtract 4 * number.

Let’s see the example of subtracting value from the pointer variable on 64-bit architecture.

  1. #include<stdio.h>  
  2. int main(){  
  3. int number=50;        
  4. int *p;//pointer to int      
  5. p=&number;//stores the address of number variable        
  6. printf(“Address of p variable is %u \n”,p);        
  7. p=p-3; //subtracting 3 from pointer variable    
  8. printf(“After subtracting 3: Address of p variable is %u \n”,p);        
  9. return 0;  
  10. }    

Output

Address of p variable is 3214864300 After subtracting 3: Address of p variable is 3214864288

You can see after subtracting 3 from the pointer variable, it is 12 (4*3) less than the previous address value.

However, instead of subtracting a number, we can also subtract an address from another address (pointer). This will result in a number. It will not be a simple arithmetic operation, but it will follow the following rule.

If two pointers are of the same type,

  1. Address2 – Address1 = (Subtraction of two addresses)/size of data type which pointer points  

Consider the following example to subtract one pointer from an another.

  1. #include<stdio.h>  
  2. void main ()  
  3. {  
  4.     int i = 100;   
  5.     int *p = &i;  
  6.     int *temp;  
  7.     temp = p;   
  8.     p = p + 3;  
  9.     printf(“Pointer Subtraction: %d – %d = %d”,p, temp, p-temp);  
  10. }  

Output

Pointer Subtraction: 1030585080 – 1030585068 = 3

Illegal arithmetic with pointers

There are various operations which can not be performed on pointers. Since, pointer stores address hence we must ignore the operations which may lead to an illegal address, for example, addition, and multiplication. A list of such operations is given below.

  • Address + Address = illegal
  • Address * Address = illegal
  • Address % Address = illegal
  • Address / Address = illegal
  • Address & Address = illegal
  • Address ^ Address = illegal
  • Address | Address = illegal
  • ~Address = illegal

Pointer to function in C

As we discussed in the previous chapter, a pointer can point to a function in C. However, the declaration of the pointer variable must be the same as the function. Consider the following example to make a pointer pointing to the function.

  1. #include<stdio.h>  
  2. int addition ();  
  3. int main ()  
  4. {  
  5.     int result;   
  6.     int (*ptr)();  
  7.     ptr = &addition;  
  8.     result = (*ptr)();  
  9.     printf(“The sum is %d”,result);  
  10. }  
  11. int addition()  
  12. {  
  13.     int a, b;   
  14.     printf(“Enter two numbers?”);  
  15.     scanf(“%d %d”,&a,&b);  
  16.     return a+b;  
  17. }  

Output

Enter two numbers?10 15 The sum is 25

Pointer to Array of functions in C

To understand the concept of an array of functions, we must understand the array of function. Basically, an array of the function is an array which contains the addresses of functions. In other words, the pointer to an array of functions is a pointer pointing to an array which contains the pointers to the functions. Consider the following example.

  1. #include<stdio.h>  
  2. int show();  
  3. int showadd(int);  
  4. int (*arr[3])();  
  5. int (*(*ptr)[3])();  
  6.   
  7. int main ()  
  8. {  
  9.     int result1;  
  10.     arr[0] = show;  
  11.     arr[1] = showadd;  
  12.     ptr = &arr;  
  13.     result1 = (**ptr)();  
  14.     printf(“printing the value returned by show : %d”,result1);  
  15.     (*(*ptr+1))(result1);  
  16. }  
  17. int show()  
  18. {  
  19.     int a = 65;  
  20.     return a++;  
  21. }  
  22. int showadd(int b)  
  23. {  
  24.     printf(“\nAdding 90 to the value returned by show: %d”,b+90);  
  25. }  

Output

printing the value returned by show : 65 Adding 90 to the value returned by show: 155

Dynamic memory allocation in C

The concept of dynamic memory allocation in c language enables the C programmer to allocate memory at runtime. Dynamic memory allocation in c language is possible by 4 functions of stdlib.h header file.

  1. malloc()
  2. calloc()
  3. realloc()
  4. free()

Before learning above functions, let’s understand the difference between static memory allocation and dynamic memory allocation.static memory allocationdynamic memory allocationmemory is allocated at compile time.memory is allocated at run time.memory can’t be increased while executing program.memory can be increased while executing program.used in array.used in linked list.

Now let’s have a quick look at the methods used for dynamic memory allocation.malloc()allocates single block of requested memory.calloc()allocates multiple block of requested memory.realloc()reallocates the memory occupied by malloc() or calloc() functions.free()frees the dynamically allocated memory.


malloc() function in C

The malloc() function allocates single block of requested memory.It doesn’t initialize memory at execution time, so it has garbage value initially.It returns NULL if memory is not sufficient.

The syntax of malloc() function is given below:

  1. ptr=(cast-type*)malloc(byte-size)  

Let’s see the example of malloc() function.

  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. int main(){  
  4.   int n,i,*ptr,sum=0;    
  5.     printf(“Enter number of elements: “);    
  6.     scanf(“%d”,&n);    
  7.     ptr=(int*)malloc(n*sizeof(int));  //memory allocated using malloc    
  8.     if(ptr==NULL)                         
  9.     {    
  10.         printf(“Sorry! unable to allocate memory”);    
  11.         exit(0);    
  12.     }    
  13.     printf(“Enter elements of array: “);    
  14.     for(i=0;i<n;++i)    
  15.     {    
  16.         scanf(“%d”,ptr+i);    
  17.         sum+=*(ptr+i);    
  18.     }    
  19.     printf(“Sum=%d”,sum);    
  20.     free(ptr);     
  21. return 0;  
  22. }    

Output:Enter elements of array: 3 Enter elements of array: 10 10 10 Sum=30


calloc() function in C

The calloc() function allocates multiple block of requested memory.It initially initialize all bytes to zero.It returns NULL if memory is not sufficient.

The syntax of calloc() function is given below:

  1. ptr=(cast-type*)calloc(number, byte-size)  

Let’s see the example of calloc() function.

  1. #include<stdio.h>  
  2. #include<stdlib.h>  
  3. int main(){  
  4.  int n,i,*ptr,sum=0;    
  5.     printf(“Enter number of elements: “);    
  6.     scanf(“%d”,&n);    
  7.     ptr=(int*)calloc(n,sizeof(int));  //memory allocated using calloc    
  8.     if(ptr==NULL)                         
  9.     {    
  10.         printf(“Sorry! unable to allocate memory”);    
  11.         exit(0);    
  12.     }    
  13.     printf(“Enter elements of array: “);    
  14.     for(i=0;i<n;++i)    
  15.     {    
  16.         scanf(“%d”,ptr+i);    
  17.         sum+=*(ptr+i);    
  18.     }    
  19.     printf(“Sum=%d”,sum);    
  20.     free(ptr);    
  21. return 0;  
  22. }    

Output:Enter elements of array: 3 Enter elements of array: 10 10 10 Sum=30


realloc() function in C

If memory is not sufficient for malloc() or calloc(), you can reallocate the memory by realloc() function. In short, it changes the memory size.

Let’s see the syntax of realloc() function.

  1. ptr=realloc(ptr, new-size)  

free() function in C

The memory occupied by malloc() or calloc() functions must be released by calling free() function. Otherwise, it will consume memory until program exit.

Let’s see the syntax of free() function.

  1. free(ptr)