C - Pointers



Pointers in C are used to work with memory directly, enabling efficient memory management and handling more complex data structures. With pointers, you can access and modify data located in memory, pass data efficiently between functions, and create dynamic data structures like linked lists, trees, and graphs.

A pointer references a location in memory, and obtaining the value stored at that location is known as dereferencing the pointer.

Pointers are used to pass parameters by reference. This is useful if the programmer wants a function's modifications to a parameter to be visible to the function's caller. This is also useful for returning multiple values from a function.

Pointer is a variable that stores the reference to another variable, which may be of any type, such as int, float or char, an array (one or multidimensional), struct or union, or even a pointer type itself.

In C, it is important to understand the purpose of two operators in the context of pointer mechanism.

The & operator: Address of operator

The * operator: Dereference operator

To declare a pointer variable, the following syntax is to be used −

type *var;

The name of the variable must be prefixed with an asterisk (*). The data type indicates it can store the address of which data type. For example

int *x;

In this case, the variable x is meant to store the address of another int variable.

float *y;

The y variable is a pointer that stores the memory location of a float variable.

The & operator returns the address of an existing variable. We can assign it to the pointer variable

int a;
int *x = &a;

Assuming that the compiler creates the variable at the address 1000, and x at the address 2000, address of a is stored in x.

variable at the address

The deference operator returns the value at the address stored in the pointer variable. Here *x will return the value of a i.e. the value stored in memory address 1000, which in fact is the value of x.

Let us understand this with the help of following example. An int variable is declared below. Its value and address are displayed as below −

Example

#include <stdio.h>
int main(){
   int var = 100;
   printf("var: %d address: %d", var, &var );
}

Output

var: 100 address: 6422044

We can also use %p format specifier to obtain the hexadecimal number of the memory address

Example

#include <stdio.h>
int main(){
   int var = 100;
   printf("var: %d address: %p", var, &var );
}

Output

var: 100 address: 000000000061FE1C

The address of var is stored in the intptr variable with & operator

Example

#include <stdio.h>
int main(){
   int var = 100;
   int *intptr = &var;
   printf("var: %d address of var: %d intptr: %ld address of intptr: %d", var, &var, intptr, &intptr );
}

Output

var: 100 address of var: 566775836 intptr: 140733760163868 address of intptr: 566775840

Take an example of a float variable and find its address

Example

#include <stdio.h>
int main(){
   float var1 = 10.55;
   printf("var1: %f address of var1: %d", var1, &var1);
}

Output

var1: 10.550000 address of var1: 6422044

We can see that the address of this variable (any type of variable for that matter) is an integer. So, if we try to store it in a pointer variable of int type, see what happens −

float var1 = 10.55;
int *intptr = &var1;

The compiler doesn’t accept this, and reports the following error −

initialization of 'int *' from incompatible pointer type 
'float *' [-Wincompatible-pointer-types]

It indicates that the type of a variable and the type of its pointer must be same.

In C, variables have specific data types that define their size and how they store values. Declaring a pointer with a matching type (e.g., float *) enforces type compatibility between the pointer and the data it points to.

Different data types occupy different amounts of memory in C. For example, an int typically takes 4 bytes, while a float might take 4 or 8 bytes depending on the system.

Adding or subtracting integers from pointers moves them in memory based on the size of the data they point to.

Hence, we declare the floatptr variable of float * type.

Example

#include <stdio.h>
int main(){
   float var1 = 10.55;
   float *floatptr = &var1;
   printf("var1: %f address of var1: %d floatptr: %d address of floatptr: %d", var1, &var1, floatptr, &floatptr );
}

Output

var1: 10.550000 address of var1: 6422044 floatptr: 6422044 address of floatptr: 6422032

The * operator is called Dereference operator. It returns the value stored in the address which stored in the pointer, i.e., the value of the variable it is pointing to.

Example

#include <stdio.h>
int main(){
   float var1 = 10.55;
   float *floatptr = &var1;
   printf("var1: %f address of var1: %d\n",var1, &var1);
   printf("floatptr: %d address of floatptr: %d\n", floatptr, &floatptr );
   printf("var1: %f value at floatptr: %f", var1, *floatptr);
}

Output

var1: 10.550000 address of var1: 6422044
floatptr: 6422044 address of floatptr: 6422032
var1: 10.550000 value at floatptr: 10.550000

We may have a pointer variable that stores the address of another pointer itself.

Pointer Variable

In the above figure, a is a normal int variable, whose pointer is x. In turn, the variable stores the address of x. Note that y is declared as int **, to indicate that it is a pointer to another pointer variable. Obviously, y will return the address of x and *y is the value in x (which is the address of a). To obtain value of a from y, we need to use **y expression. Usually, y will be called as pointer to a pointer.

Example

#include <stdio.h>
int main(){
   int var = 10;
   int *intptr = &var;
   int **ptrptr = &intptr;
   printf("var: %d address of var: %d\n",var, &var);
   printf("inttptr: %d address of inttptr: %d\n", intptr, &intptr );
   printf("var: %d value at intptr: %d\n", var, *intptr);
   printf("ptrptr: %d address of ptrtptr: %d\n", ptrptr, &ptrptr );
   printf("intptr: %d value at ptrptr: %d\n", intptr, *ptrptr);
   printf("var: %d *intptr: %d **ptrptr: %d\n", var, *intptr, **ptrptr);
   return 0;
}

Output

var: 10 address of var: 6422044
inttptr: 6422044 address of inttptr: 6422032
var: 10 value at intptr: 10
ptrptr: 6422032 address of ptrtptr: 6422024
intptr: 6422044 value at ptrptr: 6422044
var: 10 *intptr: 10 **ptrptr: 10

You can have a pointer to an array as well as a derived type defined with struct. Pointers have important applications. Thet are used while calling a function by passing the reference. They also help in overcoming the limitation of a function’s ability to return only a single value. With pointers, you can obtain the effect of returning multiple values or arrays.

Advertisements