What is a Pointer in C?
A pointer is a special type of variable that stores the memory address of another variable instead of storing the actual value. In other words, a pointer "points" to the memory location where data is stored.
Like any other variable, a pointer also has its own name and occupies memory. However, unlike ordinary variables that store values such as integers, floating-point numbers, or characters, a pointer stores only the address of another variable.
Pointers are one of the most powerful features of the C programming language. They provide direct access to memory, making programs more efficient and enabling advanced concepts such as dynamic memory allocation, file handling, linked lists, trees, and function pointers.
A pointer is a variable that stores the memory address of another variable rather than storing the actual value.
Example:
Suppose the following variable is declared:
int num = 25;If the variable num is stored at memory location 1000, then a pointer variable can store the value 1000, which represents the address of num.
Why Do We Need Pointers?
A common question among beginners is why pointers are required when variables can already store values. The answer is that many programming tasks become difficult or impossible without pointers.
Pointers allow a program to work directly with memory locations instead of copying values repeatedly. This improves execution speed, reduces memory usage, and makes it possible to implement several advanced programming techniques.
Major Uses of Pointers
- Access memory directly.
- Pass arrays and structures efficiently to functions.
- Return multiple values from a function.
- Allocate memory dynamically using
malloc(),calloc(), andrealloc(). - Create dynamic data structures such as linked lists, stacks, queues, and trees.
- Perform efficient file handling.
- Implement callback functions and function pointers.
- Improve program performance by avoiding unnecessary copying of data.
Understanding Memory Representation
Every variable declared in a program is stored at a unique memory location. The variable stores a value, while the memory location where it is stored is called its address.
A pointer simply stores this address instead of the actual value.
| Variable | Stored Value | Memory Address |
|---|---|---|
num | 25 | 1000 |
ptr | 1000 | 2000 |
num stores the value 25, whereas the pointer ptr stores the address 1000, which is the location where num is stored.Pointer Declaration in C
Before using a pointer, it must be declared just like any other variable. A pointer declaration tells the compiler the type of data whose address the pointer will store.
A pointer declaration consists of three parts:
- The data type of the variable whose address will be stored.
- An asterisk (
*), which indicates that the variable is a pointer. - The pointer variable name.
Syntax:
data_type *pointer_name;Examples of Pointer Declaration:
int *ptr1;
char *ptr2;
float *ptr3;
double *ptr4;Each pointer is capable of storing the address of a variable having the corresponding data type.
| Pointer Declaration | Meaning |
|---|---|
int *ptr; | Pointer to an integer variable. |
char *ptr; | Pointer to a character variable. |
float *ptr; | Pointer to a floating-point variable. |
double *ptr; | Pointer to a double-precision variable. |
Pointer Naming Rules
A pointer variable follows the same naming rules as any other variable in C. You can choose any meaningful name, provided it follows the identifier rules of the language.
Valid Pointer Names
int *ptr;
int *numberPointer;
float *salaryPtr;
char *studentName;studentPtr, empPtr, or head instead of using meaningless names like x or p1. Meaningful names improve code readability and make programs easier to understand.Initialization of a Pointer Variable
Declaring a pointer only creates the pointer variable; it does not assign any valid memory address to it. Before using a pointer, it should always be initialized with the address of an existing variable or with NULL.
Using an uninitialized pointer is unsafe because it may contain an unpredictable memory address, leading to undefined behavior or program crashes.
Example
int num = 26;
int *ptr;
ptr = #In the above example:
numstores the value 26.&numreturns the memory address ofnum.- The pointer
ptrstores this memory address.
The Address-of Operator (&)
The address-of operator (&) is a unary operator that returns the memory address of a variable. It is commonly used while assigning an address to a pointer.
Example
int num = 50;
printf("%p", &num);The expression &num returns the memory address where the variable num is stored.
& operator does not return the value stored in a variable. Instead, it returns the memory address of that variable.The Dereference Operator (*)
The asterisk (*) is known as the dereference operator or indirection operator. It is used to access the value stored at the memory address held by a pointer.
Example
int num = 50;
int *ptr = #
printf("%d", *ptr);Although ptr stores the address of num, the expression *ptr retrieves the value stored at that address.
| Expression | Meaning |
|---|---|
ptr | Stores the memory address of the variable. |
*ptr | Returns the value stored at that memory address. |
&num | Returns the address of num. |
num | Returns the actual value stored in the variable. |
- The
&operator returns the address of a variable. - A pointer stores that address.
- The
*operator accesses the value stored at the address contained in the pointer.
Understanding Pointers with Memory Representation
To understand pointers properly, it is important to know how variables are stored in memory. Whenever a variable is declared, the compiler reserves some memory for it. Every memory location has a unique address, and the variable stores its data at that address.
A pointer does not store the actual data. Instead, it stores the memory address where the data is located. By using this address, the pointer can indirectly access or modify the value of the variable.
Suppose an integer variable num stores the value 25. If the compiler allocates memory address 1000 to this variable, then a pointer can store the value 1000, which is the address of num.
First Pointer Program
The following program demonstrates how a pointer stores the address of a variable and how the value can be accessed using the dereference operator.
#include <stdio.h>
int main()
{
int num = 25;
int *ptr;
ptr = #
printf("Value of num = %d\n", num);
printf("Address of num = %p\n", &num);
printf("Value stored in ptr = %p\n", ptr);
printf("Value using *ptr = %d\n", *ptr);
return 0;
}Understanding Each Expression
The following table summarizes the meaning of the most commonly used pointer expressions.
| Expression | Returns |
|---|---|
num | The actual value stored in the variable. |
&num | The memory address of the variable. |
ptr | The address stored inside the pointer. |
*ptr | The value available at the address stored in the pointer. |
&ptr | The memory address of the pointer variable itself. |
numgives the value.&numgives the address of the variable.ptrstores that address.*ptraccesses the value stored at that address.&ptrreturns the address of the pointer variable.
Modifying a Variable Using a Pointer
One of the biggest advantages of pointers is that they allow us to modify the value of a variable indirectly. Instead of changing the variable directly, we can access its memory location through a pointer and update the value stored there.
Since a pointer stores the address of a variable, any modification made through the pointer is actually performed on the original variable.
A pointer does not create a copy of a variable. It simply stores the address of the original variable. Therefore, changing the value using the pointer also changes the original variable.
Program: Changing the Value Through a Pointer
#include<stdio.h>
int main()
{
int num = 25;
int *ptr = #
printf("Before Modification = %d\n", num);
*ptr = 100;
printf("After Modification = %d\n", num);
return 0;
}Another Example
A pointer can be used to update the value of a variable multiple times. Every change made through the pointer is immediately reflected in the original variable.
#include<stdio.h>
int main()
{
int marks = 70;
int *ptr = &marks;
printf("Original Marks = %d\n", marks);
*ptr = 85;
printf("Updated Marks = %d\n", marks);
return 0;
}Output
Original Marks = 70
Updated Marks = 85marks. When the statement *ptr = 85; is executed, the value stored in marks changes from 70 to 85.Direct Modification vs Pointer Modification
| Direct Method | Using Pointer |
|---|---|
num = 100; | *ptr = 100; |
| Variable is accessed directly. | Variable is accessed indirectly through its memory address. |
| Simple and straightforward. | Useful when the address of the variable is available instead of the variable itself. |
Important Points
- A pointer stores the address of a variable, not its value.
- The dereference operator (
*) is used to access or modify the value stored at that address. - Changing
*ptrchanges the original variable because both refer to the same memory location. - The memory address stored inside the pointer remains unchanged unless a new address is assigned to it.
Size of a Pointer in C
One of the most common misconceptions among beginners is that the size of a pointer depends on the type of data it points to. In reality, the size of a pointer is determined by the computer architecture (or memory model), not by the data type.
Whether a pointer stores the address of an int, char, float, or double, it always stores only a memory address. Since every memory address on a particular system has the same size, all pointer variables occupy the same amount of memory.
Pointer Size on Different Architectures
The following table shows the typical size of pointers on different computer architectures.
| Architecture | Typical Pointer Size | Description |
|---|---|---|
| 16-bit | 2 Bytes | Older systems and compilers such as Turbo C. |
| 32-bit | 4 Bytes | Most legacy desktop operating systems. |
| 64-bit | 8 Bytes | Modern operating systems and computers. |
Program: Finding the Size of Different Pointers
The following program demonstrates that pointers of different data types occupy the same amount of memory.
#include<stdio.h>
int main()
{
int *iptr;
char *cptr;
float *fptr;
double *dptr;
printf("Size of int pointer = %u Bytes\n", sizeof(iptr));
printf("Size of char pointer = %u Bytes\n", sizeof(cptr));
printf("Size of float pointer = %u Bytes\n", sizeof(fptr));
printf("Size of double pointer = %u Bytes\n", sizeof(dptr));
return 0;
}Why Do All Pointers Have the Same Size?
Regardless of the data type, every pointer stores only a memory address. Since all memory addresses on a particular architecture require the same number of bytes, every pointer occupies the same amount of memory.
The data type associated with a pointer is used only to tell the compiler how to interpret the data stored at that address. It does not affect the amount of memory required to store the address itself.
int *stores the address of an integer.char *stores the address of a character.float *stores the address of a floating-point number.double *stores the address of a double-precision number.
Although these pointers refer to different data types, each of them stores only a memory address. Therefore, their sizes remain the same.
Common Misconceptions
| Statement | Correct? | Explanation |
|---|---|---|
An int * occupies more memory than a char *. | No | Both pointers store only an address, so their sizes are the same. |
| Pointer size depends on the data type. | No | Pointer size depends only on the system architecture. |
| All pointers have the same size on a given computer. | Yes | Every memory address has the same size on the same architecture. |
Types of Pointers in C (Basic Introduction)
Depending on their state and usage, pointers can behave differently during program execution. Some pointers work correctly, while others may lead to unexpected behavior or even program crashes if not handled carefully.
The most commonly used pointer types are listed below:
- NULL Pointer
- Wild Pointer
- Dangling Pointer
- Void Pointer (Generic Pointer)
Let's understand each of them one by one.
1. NULL Pointer
A NULL pointer is a pointer that does not point to any valid memory location. Instead of storing the address of a variable, it stores the special value NULL.
Initializing a pointer with NULL is considered a good programming practice because it clearly indicates that the pointer is currently not pointing to any valid object.
Syntax:
int *ptr = NULL;Example:
#include <stdio.h>
int main()
{
int *ptr = NULL;
printf("%p", ptr);
return 0;
}Important: A NULL pointer should never be dereferenced. Attempting to access *ptr when ptr is NULL results in undefined behavior and may crash the program.
2. Wild Pointer
A wild pointer is a pointer that has been declared but has not been initialized with a valid memory address. Since it contains an unknown (garbage) address, using it may lead to unpredictable results.
Example:
int *ptr;In the above declaration, the pointer exists but does not point to any valid memory location.
Incorrect Usage
int *ptr;
*ptr = 10;// Dangerous3. Dangling Pointer
A dangling pointer is a pointer that refers to a memory location that is no longer valid. This situation commonly occurs when dynamically allocated memory is freed, but the pointer still stores the old address.
Example:
int *ptr;
ptr = (int *)malloc(sizeof(int));
free(ptr);
/* ptr is now a dangling pointer */After calling free(), the allocated memory is released, but ptr still contains the old address. Accessing this address results in undefined behavior.
NULL to the pointer after freeing the memory.
free(ptr);
ptr = NULL;4. Void Pointer (Generic Pointer)
A void pointer, also known as a generic pointer, can store the address of variables belonging to any data type. Unlike typed pointers, it is not associated with a specific data type.
Example
int num = 25;
void *ptr = #Since a void pointer has no type information, it cannot be dereferenced directly. Before accessing the stored value, it must first be converted to the appropriate pointer type.
printf("%d",*(int*)ptr);Comparison of Different Pointer Types
| Pointer Type | Description | Safe to Use? |
|---|---|---|
| NULL Pointer | Does not point to any valid memory location. | Yes, until dereferenced. |
| Wild Pointer | Contains an uninitialized memory address. | No. |
| Dangling Pointer | Points to memory that has already been released. | No. |
| Void Pointer | Can store the address of any data type. | Yes, after proper typecasting. |
Pointer Arithmetic in C
A pointer stores the memory address of a variable. Just like numeric variables, pointers also support certain arithmetic operations. These operations are known as pointer arithmetic.
Pointer arithmetic allows a pointer to move from one memory location to another. It is widely used while working with arrays, strings, dynamic memory allocation, and data structures such as linked lists.
Operations Allowed on Pointers
The following arithmetic operations can be performed on pointers:
| Operation | Description |
|---|---|
ptr+n | Moves the pointer forward by n elements. |
ptr-n | Moves the pointer backward by n elements. |
ptr1-ptr2 | Returns the number of elements between two pointers. |
*), division (/), and modulus (%) are not valid for pointers.Why Does Not ptr+1 Increase by One Byte?
This is one of the most frequently asked interview questions.
When we add 1 to a pointer, the compiler automatically increases the address by the size of the data type to which the pointer points.
| Pointer Type | Typical Size | Result of ptr + 1 |
|---|---|---|
char * | 1 Byte | Address increases by 1 byte. |
int * | 4 Bytes | Address increases by 4 bytes. |
float * | 4 Bytes | Address increases by 4 bytes. |
double * | 8 Bytes | Address increases by 8 bytes. |
Pointer Arithmetic with Arrays
Pointer arithmetic becomes especially useful while working with arrays because array elements are stored in contiguous memory locations.
int arr[]={11,22,33,44};
int *ptr=arr;
printf("%d\n",*ptr);
printf("%d\n",*(ptr+1));
printf("%d\n",*(ptr+2));
printf("%d\n",*(ptr+3));Output
11
22
33
44