• Articles
  • Tutorials
  • Interview Questions
  • Webinars

Pointers in C with Types and Examples

Pointers in C with Types and Examples

This blog covers all aspects of pointers in C. First, you’ll learn about the initialization and size of pointers. Afterward, we will discuss the types, use cases, advantages, and disadvantages of pointers in C. The concept of call by value and call by reference is also discussed in this blog.

Table of Contents

Watch the video below to learn the fundamentals of C:

Definition of Pointer in C

Pointers in C

Every variable is stored in some memory location, and that memory location has an address. A pointer is a variable that stores the memory address of variables, functions, or other pointers. 

A pointer is a derived data type that can store the memory address of other variables in C programming. Using the pointer, we can access and modify the data stored in that memory address.

As it stores the memory address, its size does not depend on the data type of variable it points to. It depends on the architecture of the system. For example, the size of a pointer in a 32-bit architecture is 2 bytes.  

A pointer is another way of passing parameters, i.e., pass-by address. It supports dynamic memory allocation

Syntax and Initialization

The pointer initialization can be divided into three parts, i.e., declaration, initialization, and dereferencing.

1. Declaration

As we declare a variable, we need to declare the pointer in the C programming language. 

The syntax for declaring a pointer in C is as follows: 

data_type *name_of_the_pointer;

Here, data_type is the type of data the pointer is pointing to. The asterisk sign (*) is called the indirection or dereferencing operator. It serves as a pointer’s representation. It informs the compiler that this variable references a memory address, not a regular integer-type variable.

See the following example demonstrating the declaration of a pointer in C:

int  *ptr;

Or

int*  ptr;

Or

int  *  ptr;

All these representations have the same meaning. Here, int states that the pointer is pointing to an integer value, and “ptr” is the name of the pointer. We cannot declare multiple pointers in a single step in the same way we declare a variable, i.e., 

int *x, y, z;

This expression states that x is a pointer, and y and z are normal integer variables. The correct way to declare three integer type pointers in a single step is as follows:

int *x, *y, *z;

2. Initialization

After the declaration, we need to initialize a pointer. The following example represents the initialization of the pointer:

int x=45;
int *ptr;
ptr=&x;

These expressions state that the value 45 is assigned to the variable x, and the pointer “ptr” stores the address of the variable x. The & (addressof) operator is used to obtain the memory address of the variable x. A pointer can also be declared and initialized in a single step. It is called the pointer definition. 

int x=45;
int *ptr=&x;

It should be noted that the data type of the variable and the pointer should be the same. The following example illustrates the declaration and initialization of a pointer:

#include <stdio.h>
void intellipaat()
{
int x=45;
int *ptr;
ptr=&x;
printf("Value of x = %d \n", x);
printf("Value at ptr = %p \n", ptr);
}
// Driver program
int main()
{
intellipaat();
return 0;
}

We get the following output from this program:

Value at x = 45 
Value at ptr = 0x7ffe959fddc4 

3. Dereferencing the Pointer

Dereferencing a pointer means accessing the value stored in the memory address that the pointer is pointing to. This is done with the help of the indirection or dereferencing operator (*). With the help of dereferencing, we can also update the value stored in that memory address. See the following example to understand it in a better way:

#include <stdio.h>
void intellipaat()
{
int x=45;
int *ptr;
ptr=&x;
//updating the value of x by dereferencing the pointer
*ptr=46;
printf("Value of x = %d \n", x);
printf("Value at ptr = %p \n", ptr);
}
// Driver program
int main()
{
intellipaat();
return 0;
}

In this program, we are accessing the value of x by dereferencing the pointer variable “ptr.” It will give the following output:

Value of x = 46 
Value at ptr = 0x7ffc1dbe2cc4 

Size of Pointers in C with code

The size of the Pointers is not fixed and it is independent of data type. It depends on the size of the different factors such as CPU architecture and the word size of the processor. Determining the size of a pointer is required if you want to know how many bytes of memory are occupied in the memory space. For example, for a 32-bit computer, the size of the pointer can be 4 bytes and for a 64-bit computer, it can be 8 bytes. 

To calculate the size of a pointer the syntax is:

sizeof(datatype/data)

The following example illustrates how to calculate the size of a pointer that stores an integer variable:

#include<stdio.h>
int main() {
  int x = 10;
  int *ptr = &x; // pointer variable holding address 
  //printing size of integer variable
  printf("The size of the integer pointer is %ld bytes", sizeof(ptr));
}

This program gives the following output:

The size of the integer pointer is 8 bytes

Why Do We Need Pointers In C

As we have already learned when we use a pointer in C, we are dealing with the memory address directly. This means you can access and manipulate the data stored in those memory locations more efficiently than accessing it indirectly through variable names. In simple words, pointers allow us to manipulate data in the memory of the computer. Pointers make it easier to handle complex data structures and efficient management of the memory. When parameters are passed to functions, pointers can directly modify the original data rather than just working with a copy. Moreover, these help reduce the program size and enhance the performance. 

Types of Pointers in C

There are various types of pointers in C. The following section explains the different types of pointers in C.

Integer Pointer

These are the pointers that point to the integer values. In other words, they store the address of integer variables. These are also known as Pointer to Integer. The syntax for creating a pointer to an integer is:

int *pointer_name;

The following example shows how to create an integer pointer in C:

#include<stdio.h>
int main()
{
int x = 25;
int *ptr;
ptr = &x;
printf("The value of x is = %d\n",x);
printf("Address of x is = %x\n",&x);
printf("The Pointer points to the address = %x\n", ptr);
return 0;
}

The output of this program is:

The value of x is = 25
Address of x is = 6967bb84
The Pointer points to the address = 6967bb84

Array Pointer

An array pointer, or pointer to an array, stores the address of the array. To create a pointer to an array, the following syntax is followed:

data_type (*var_name)[size_of_array];

The pointer that points to the first element of the array and the one that points to the whole array are different. Because the first value is a single integer. The following example will help you understand this concept clearly:

//Example to understand a pointer to an array of pointers. 
#include<stdio.h>
int main()
{
int *x;
int arr[6];
// An array pointer
int (*ptr)[6]; 
// Points to 0th element of the arr.
x = arr;
// Points to the entire array arr.
ptr = &arr; 
printf("x = %p, ptr = %p\n", x, ptr);
x++; 
ptr++;
printf("x = %p, ptr = %p\n", x, ptr);
return 0;
}

Here, x points to the integer value, and ptr points to the array of 6 integers. Both have different values, as shown in the output:

x = 0x7ffdf5b01580, ptr = 0x7ffdf5b01580
x = 0x7ffdf5b01584, ptr = 0x7ffdf5b01598

Structure Pointers

These are the pointers that point to the address of a structure, a user-defined data type. With the help of structure pointers, complex data structures like linked lists, trees, graphs, etc., are created. The syntax of a structure pointer in C is as follows:

struct struct_name *ptr;

The following example shows the implementation of the structure pointer:

// C program to demonstrate structure pointer
#include <stdio.h>
struct point {
int value;
};
int main()
{
struct point s;
// Initialization of the structure pointer
struct point* ptr = &s;
    //printing the value of the structure pointer
    printf("ptr = %p\n", ptr);
return 0;
}

Output:

ptr = 0x7ffd316f46e4

Function Pointer

These pointers point to functions rather than some data type like int, char, etc. We cannot allocate or deallocate memory in the case of function pointers.

The following is the syntax for the declaration of a function pointer in C:

return type (*ptr_name)(type1, type2…);  

The following example shows the implementation of the function pointer:

#include <stdio.h>
void display(int x)

printf("Value of x is %d\n", x); 

int main() 

// fun_ptr is a pointer to function display() 
void (*fun_ptr)(int) = &display; 
// Invoking display() using fun_ptr 
(*fun_ptr)(5); 
//printing the value of the function pointer
 printf("Value of fun_ptr is %d\n", fun_ptr); 
return 0; 

The output is as follows:

Value of x is 5
Value of fun_ptr is 4198694

Check out these top C and Data Structure interview questions to prepare for the interview.

Null Pointers

The null pointer does not refer to any location. When a pointer variable hasn’t yet been assigned a proper memory address, it is used to initialize it. In advanced data structures like trees, linked lists, etc., null pointers indicate the end.

The syntax of a null pointer in C is as follows:

type pointer_name = NULL;

or

type pointer_name = 0;

An example of a NULL pointer in C is as follows:

 #include <stdio.h> 
int main() 

// declaring a null pointer 
int* x = NULL; 
// dereferencing only if the pointer has any value 
if (x == NULL) { 
printf("Pointer does not point to any value"); 

else { 
printf("Value pointed by pointer: %d", *x); 

return 0; 
}

The output of this program is as follows:

Pointer does not point to any value

Void Pointers

A void pointer can hold the address of any data type, as it does not have a specific data type associated with it. The syntax for declaring a void pointer in C is as follows:

void *pointer_name = &variable_name;

To implement the void pointer in C, see the following example:

// C Program to demonstrate that a void pointer
// can hold the address of a variable of any data type
#include <stdio.h>
int main()
{
int a = 10;
char b = 'x';
// void pointer holds address of int 'a'
void* p = &a;
// void pointer holds address of char 'b'
p = &b;
printf("Value pointed by void pointer: %d \n", p);
printf("Value pointed by void pointer: %d", p);
}

The output of this program is as follows:

Value pointed by void pointer: -253320989 
Value pointed by void pointer: -253320989

Constant Pointers

These pointers cannot change the variable they are pointing to, which means the address stored in them will remain constant. The syntax to declare a constant pointer in C is as follows:

int *const ptr;  

The following example shows how to use constant pointers in C:

#include <stdio.h> 
int main()  
{  
    int x=11;  
    int y=22;  
    int *const ptr; 
    ptr=&x;  
    ptr=&y;  
    printf("Value of ptr is :%d", *ptr);  
    return 0;  
}  

This program will raise an error because we assign two addresses to a single constant pointer. The output looks like this:

/tmp/LwqxXsn8k9.c: In function ‘main’:
/tmp/LwqxXsn8k9.c:7:7: error: assignment of read-only variable ‘ptr’
    7 |    ptr=&a;
      |       ^
/tmp/LwqxXsn8k9.c:8:8: error: assignment of read-only variable ‘ptr’
    8 |     ptr=&b;
      |        ^

Pointer to Constant

In C, a “pointer to constant” refers to a situation where a pointer variable is declared to point to a constant value, meaning the value it points to cannot be modified through that pointer. This concept involves the use of the const keyword in the declaration. The following example will help you understand the concept clearly.

#include <stdio.h>
int main() {
    // Declare a constant integer
    const int myConstant = 42;
    // Declare a pointer to a constant integer
    const int *ptrToConstant;
    // Assign the address of the constant integer to the pointer
    ptrToConstant = &myConstant;
    // Accessing the value through the pointer
    printf("Value through pointer: %d\n", *ptrToConstant);
    // Trying to modify the value through the pointer will result in a compilation error
    // *ptrToConstant = 99; // This line will cause a compilation error
    return 0;
}

In this example, ptrToConstant is declared as a pointer to a constant integer using the const int * syntax. This means that ptrToConstant can point to a constant integer, but any attempt to modify the value it points to will result in a compilation error.

Check out this C Programming Tutorial to get more knowledge.

Wild Pointers

In C, a “wild pointer” refers to a pointer that has not been initialized or is pointing to an undefined memory location. Using or dereferencing such a pointer can lead to unpredictable behavior and system crashes. Wild pointers are considered a type of undefined behavior and can be a source of serious bugs in C programs.

#include <stdio.h>
int main() {
    int *wildPointer; // Declaration without initialization
    // Attempting to dereference the wild pointer
    printf("Value at wildPointer: %d\n", *wildPointer);
    return 0;
}

In this example, wildPointer is declared but not initialized. When you try to dereference it (access the value it points to), you are essentially accessing an undefined memory location. This leads to an error, as the pointer’s value is undetermined.

Dangling Pointers

A pointer that does not point to a valid object or a memory location that has been deleted is known as a dangling pointer. These pointers can be understood with the help of the following example:

#include <stdio.h>
int main()
{
int *p= (int *)malloc(sizeof(int));
// After the below free call, ptr becomes a 
// dangling pointer
free(p); 
// No more dangling pointer
p = NULL;
 printf("Value of ptr is :%d",*p ); 
}

This program gives the following output:

Segmentation fault

Normalized Pointers

In C, the term “normalized pointers” typically refers to pointers that have been adjusted or standardized in some way, often in the context of pointer arithmetic or conversion. One common use case for normalization is in the context of pointer arithmetic, especially with arrays. This is a 32-bit pointer that has as much of its value in the segment register as possible.

#include <stdio.h>
int main() {
    int myArray[5] = {10, 20, 30, 40, 50};
    int *ptr = &myArray[2]; // Pointer pointing to the third element of the array
    // Perform some arithmetic operations on the pointer
    ptr += 2; // Move the pointer two elements forward
    // Ensure the pointer is normalized within the array bounds
    if (ptr >= myArray && ptr < myArray + 5) {
        // Access the value at the normalized pointer
        printf("Value at normalized pointer: %d\n", *ptr);
    } else {
        printf("Pointer out of bounds\n");
    }
    return 0;
}

This program will give us the following output:

Value at normalized pointer: 50

File Pointers

These pointers point to the address of a file. This structure is usually known as a “file control block.” It contains details like the file name, the location of the file, and the access mode of the file. It can also be used to perform read and write operations on files.

Apart from these types, there are other types of pointers, i.e., near, far, and huge pointers. In older Intel processors, the registers were 16-bit while the address bus was 20-bit wide. This mismatch meant registers couldn’t hold complete addresses. To manage this, memory was split into 64 kB segments. Concepts like near, far, and huge pointers in C were introduced to handle these segmented memory models in 16-bit Intel architectures. However, with the advances in technology and the shift to 32-bit and 64-bit architectures, these concepts have become old and are rarely used today.

Use Cases of Pointers in C

There are multiple use cases of pointers in C that are mentioned in detail, in the section below:

Pointer Arithmetic:

We can perform multiple but a limited set of arithmetic operations on a pointer. These operations are slightly different from the ones that we generally use for mathematical calculations.  We can access array elements using pointer arithmetic. These operations are mentioned in the section below:

1. Increment: We generally use the increment operator on a pointer when we want to jump from one index to the next in an array.

The syntax for performing increment operation on a pointer is

pointer_variable++;

The following example shows how to use increment operation on pointers in C:

#include <stdio.h>
int main()
{
// defining array
int arr[4] = { 34, 23, 63, 74 };
// defining the pointer to array
int* ptr_arr = arr;
// traversing array using pointer arithmetic
for (int i = 0; i < 4; i++) {
printf("%d ", *ptr_arr++);
}
return 0;
}

This code gives us the following output:

34 23 63 74 

2. Decrement: This operation is used for jumping backward in an array. In other words, we can jump from one index to the previous index in an array with this operation.

The syntax for decrement operation on pointers in C is:

The following example shows how to create a program using decrement operation on pointers in C.

#include<stdio.h>
int main()
{
int arr[3]={34, 23, 63};
int *ptr_arr;
ptr_arr = &arr[2];
for (int i=0;i<3;i++)
{
printf("Value of *ptr = %d\n", *ptr_arr);
printf("Address of *ptr = %d\n\n", ptr_arr);
ptr_arr--; 
}
}

The output of this program will be:

Value of *ptr_arr = 63
Address of *ptr_arr = -1865162464
Value of *ptr_arr = 23
Address of *ptr_arr = -1865162468
Value of *ptr_arr = 34
Address of *ptr_arr = -1865162472

3. Addition: We can add an integer to a pointer to jump from one index to the ith index in an array. When an integer value is added to the pointer, first the value is multiplied by the size of the data type and then it is added to the pointer. The syntax for this is:

pointer_var+= n; // where ‘n’ is an integer

Let’s see the code where we perform this operation on a pointer to access ith element of an array:

 #include <stdio.h>
int main() {
int arr[4] = { 34, 23, 63, 74 };
int *arr_ptr;
arr_ptr = &arr[0];
for (int i = 0; i < 4; i++) {
printf("Value of *arr_ptr = %d\n", *arr_ptr);
printf("Address of *arr_ptr = %d\n\n", arr_ptr);
arr_ptr=arr_ptr+2;
}
}

This will give us the following result:

Value of *arr_ptr = 34
Address of *arr_ptr = 1507070608
Value of *arr_ptr = 63
Address of *arr_ptr = 1507070616
Value of *arr_ptr = 0
Address of *arr_ptr = 1507070624
Value of *arr_ptr = -1298717952
Address of *arr_ptr = 1507070632

4. Subtraction: We can subtract an integer from a  pointer and jump back in the array from one index to any of its previous indices.

The syntax for this is:

pointer_var - = n; // where ‘n’ is an integer

An example showcasing the decrement operation is given below:

#include <stdio.h>
int main() {
int arr[4] = { 34, 23, 63, 74 };
int *arr_ptr;
arr_ptr = &arr[4];
for (int i = 0; i<5; i++)
{
printf("Value of *arr_ptr = %d\n", *arr_ptr);
printf("address of *arr_ptr = %d\n\n", arr_ptr);
arr_ptr-=1; 
}
}

We get the following result from this program:

Value of *arr_ptr = 74
address of *arr_ptr = 1154928556
Value of *arr_ptr = 63
address of *arr_ptr = 1154928552
Value of *arr_ptr = 23
address of *arr_ptr = 1154928548
Value of *arr_ptr = 34
address of *arr_ptr = 1154928544

Pointer to Pointer

This pointer stores the memory address of another pointer instead of pointing to a data value. The syntax for declaring this type of pointer is as follows:

datatype ** pointer_name;

To make you understand the concept better, an example is given below:

#include <stdio.h>
int main () {
   int  x;
   int  *p;
   int  **pp;
   x = 20;
   /* take the address of var */
   p = &x;
   /* take the address of ptr using the address of the operator & */
   pp = &p;
   /* take the value using pptr */
   printf("Value of var = %d\n", x );
   printf("Value available at *p = %d\n", *p );
   printf("Value available at **pp = %d\n", **pp);
   return 0;
}

The output of this program will be as follows:

Value of var = 20
Value available at *p = 20
Value available at **pp = 20

Array of Pointers

In C, a pointer array is a collection of indexed pointer variables of similar data types which references to a memory location. It is used when we want to refer to multiple memory locations of a similar data type. The data can be accessed by dereferencing the pointer that points to it.

Syntax:

data_type *array_name [array_size];

Here, data_type refers to the type of data the pointer is pointing to. An example demonstrating an array of pointers in C is given below:

#include <stdio.h>
int main()
{
// declaring some temp variables
int x1 = 1;
int x2 = 2;
int x3 = 3;
// array of pointers to integers
int* ptr_arr[3] = { &x1, &x2, &x3 };
// traversing using loop
for (int i = 0; i < 3; i++) {
printf("Value of x%d: %d\tAddress: %p\n", i + 1, *ptr_arr[i], ptr_arr[i]);
}
return 0;
}

The output that we obtain from this program is:

Value of x1: 1 Address: 0x7ffe62b6f528
Value of x2: 2 Address: 0x7ffe62b6f524
Value of x3: 3 Address: 0x7ffe62b6f520

Call by Value

In call by value, the value of the actual parameter is copied into the formal parameter. For both actual and formal parameters, different memory is allocated therefore, we can not modify the actual parameter value using formal parameters. Here, the term “actual parameter” refers to the argument that is used in the function call whereas “formal parameter” is the argument that is used in the definition of the function. The example of call by value in C is:

#include<stdio.h>  
void change(int x) {    
    printf("Value of x inside function before addition =%d \n",x);    
    x=x+100;    
    printf("Value of x inside function after addition =%d \n", x);    
}    
int main() {    
    int a=20;    
    printf("Before calling the function fun(), a=%d \n", a);    
    change(a);//passing value in function    
    printf("After calling the function fun(), a=%d \n", a);    
return 0;  
}    

Output of this program is:

Before calling the function fun(), a=20 
Value of x inside function before addition =20 
Value of x inside function after addition =120 
After calling the function fun(), a=20 

Call by Reference

In call by reference the address of the variable is passed as an actual parameter to the function using pointer. This means we can modify the actual parameter’s value by using formal parameters. In other words, If you modify the formal parameter, then it will change the value of the actual parameter as well. Inside the function, all the operations are performed on the value stored at the address of the actual parameters, and the modified value is stored at the same address. An example of call by reference in C is:

#include <stdio.h>
void fun(int *x) {    
    printf("Value of x inside function before addition=%d \n",*x);    
    (*x) += 100;    
    printf("Value of x inside function after addition=%d \n", *x);    
}      
int main() {    
    int a=20;    
    printf("Before calling the function fun(), a=%d \n", a);    
    fun(&a);//passing reference in function    
    printf("After calling the function fun(), a=%d \n", a);    
return 0;  
}    

The output of this program is:

Before calling the function fun(), a=20 
Value of x inside function before addition=20 
Value of x inside function after addition=120 
After calling the function fun(), a=120 

Advantages of Pointers in C

Following are the advantages of pointers in C:

  • Pointers make it easy to access memory location of variables in C.
  • An array or structure can be easily accessed by using pointers.
  • Dynamic memory allocation is possible because of pointers.
  • Pointers are useful for creating complex data structures such as linked lists, graphs, and trees, etc.
  • Pointers reduce the program length and time taken in execution of the program.

Disadvantages of Pointers in C

Besides the advantages of pointers mentioned above, there are disadvantages as well which are as follows:

  • Improper use of pointers can lead to security issues.
  • In case an incorrect value is provided to the pointer, it can cause memory corruption.
  • We can get segmentation errors because of uninitialized pointers.
  • The use of pointers can result in memory leakage.
  • Pointers are slower as compared to the variables in C.

Endnote

Understanding the types of pointers helps in the efficient management of memory. Pointers allow us to dynamic memory allocation and manipulate data and functions in C programming language. Because of this, performance gets optimized, and working with complex data structures becomes easier.

Got doubts? Post your questions on Intellipaat’s C community to get answers to them.

Course Schedule

Name Date Details
Python Course 29 Jun 2024(Sat-Sun) Weekend Batch
View Details
Python Course 06 Jul 2024(Sat-Sun) Weekend Batch
View Details
Python Course 13 Jul 2024(Sat-Sun) Weekend Batch
View Details

About the Author

Senior Consultant Analytics & Data Science

Presenting Sahil Mattoo, a Senior Consultant Analytics & Data Science at Eli Lilly and Company is an accomplished professional with 14 years of experience across data science, analytics, and technical leadership domains, demonstrates a remarkable ability to drive business insights. Sahil holds a Post Graduate Program in Business Analytics and Business Intelligence from Great Lakes Institute of Management.