What are Pointers in C?
Pointers in C are variables that store memory addresses. They are used to manipulate data indirectly by accessing and modifying the memory locations that they point to. Pointers provide flexibility and efficiency in programming, allowing functions to modify variables outside their scope, dynamically allocate memory, and work with complex data structures.
In C, the *
and &
symbols have special meanings when used with pointers.
1. The *
Operator:
Declaration: When declaring a pointer variable, the *
is used to indicate that the variable is a pointer. For example:
int *ptr; // Declare a pointer to an integer
Dereferencing: The *
operator is also used to dereference a pointer, which means accessing the value stored at the memory address pointed to by the pointer. For example:
int num = 10;
int *ptr = # // Assign the address of num to ptr
int value = *ptr; // Dereference the pointer to get the value
2. The &
Operator:
The &
operator is used to obtain the memory address of a variable. It returns the address where the variable is stored in memory. For example:
int num = 10;
int *ptr = # // Assign the address of num to ptr using the & operator
In the above example, &num
returns the memory address of the variable num
, and it is assigned to the pointer ptr
. This allows ptr
to “point” to the memory location of num
.
Example of Pointer:
#include <stdio.h>
int main() {
int num = 10;
int *ptr = #
printf("Value of num: %d\n", num);
printf("Value stored in ptr: %p\n", ptr);
printf("Value pointed by ptr: %d\n", *ptr);
return 0;
}
Output:
Value of num: 10
Value stored in ptr: 0x7fff5251701c
Value pointed by ptr: 10
Explanation:
- We declare an integer variable
num
and initialize it with the value 10. - Then, we declare a pointer
ptr
of typeint *
(integer pointer) and assign it the address ofnum
using the&
operator. - The first
printf
statement displays the value ofnum
. - The second
printf
statement displays the value stored inptr
, which is the memory address ofnum
. - The third
printf
statement uses the*
operator to access the value pointed byptr
, which is the value ofnum
.
1. Pointer to Array:
A pointer to an array in C holds the memory address of the first element of the array. Let’s consider an example:
#include <stdio.h>
int main() {
int arr[3] = {10, 20, 30};
int *ptr = arr;
printf("First element of arr: %d\n", *ptr);
printf("Second element of arr: %d\n", *(ptr + 1));
printf("Third element of arr: %d\n", *(ptr + 2));
return 0;
}
Output:
First element of arr: 10
Second element of arr: 20
Third element of arr: 30
Explanation
- We declare an integer array
arr
with three elements and initialize it with values. - Then, we declare a pointer
ptr
of typeint *
and assign it the address of the first element ofarr
. - The first
printf
statement uses the*
operator to access the value of the first element ofarr
using the pointerptr
. - The second and third
printf
statements demonstrate how to access the second and third elements ofarr
using pointer arithmetic.
2. Pointer to Function:
A pointer to a function in C holds the memory address of a function. It allows us to call the function indirectly through the pointer. Here’s an example:
#include <stdio.h>
void hello() {
printf("Hello, world!\n");
}
int main() {
void (*ptr)() = hello;
printf("Calling the function through the pointer:\n");
ptr();
return 0;
}
Output:
Calling the function through the pointer:
Hello, world!
Explanation
- We define a function
hello
that prints “Hello, world!” to the console. - In the
main
function, we declare a function pointerptr
using the syntaxvoid (*ptr)()
, which represents a pointer to a function that takes no arguments and returns void. - We assign the address of the
hello
function to the pointerptr
. - The
printf
statement demonstrates how to call the function indirectly through the function pointerptr
.
3. Pointer to Structure:
A pointer to a structure in C holds the memory address of a structure. It allows efficient manipulation of structure members using pointers. Here’s an example:
#include <stdio.h>
struct Person {
char name[20];
int age;
};
int main() {
struct Person person;
struct Person *ptr = &person;
strcpy(ptr->name, "ANGRY BULBASAUR");
ptr->age = 25;
printf("Name: %s\n", ptr->name);
printf("Age: %d\n", ptr->age);
return 0;
}
Output:
Name: ANGRY BULBASAUR
Age: 25
Explanation
- We define a structure named
Person
with two members:name
(an array of characters) andage
(an integer). - In the
main
function, we declare a structure variableperson
and a pointer to the structureptr
. - We assign the address of the
person
structure to the pointerptr
using the&
operator. - The
strcpy
function is used to copy the string “John” to thename
member of the structure through the pointer. - We assign the value 25 to the
age
member of the structure through the pointer. - The
printf
statements display the values of the structure members accessed through the pointer.
More Examples of Pointer:
1. Swapping Value
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
printf("Before swap: x = %d, y = %d\n", x, y);
swap(&x, &y);
printf("After swap: x = %d, y = %d\n", x, y);
return 0;
}
Output:
Before swap: x = 10, y = 20
After swap: x = 20, y = 10
Explanation:
- In this example, we define a function
swap
that takes two integer pointers as parameters. - Inside the function, we use the pointers to swap the values of the variables they point to.
- In the
main
function, we declare two integer variablesx
andy
and display their initial values. - We call the
swap
function, passing the addresses ofx
andy
. - After the swap, the values of
x
andy
are displayed again, showing that they have been exchanged.
2. Linked List Implementation:
This example demonstrates the implementation of a singly linked list using pointers in C. It involves dynamically allocating memory for each node and manipulating the list using pointers.
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
void insert(struct Node** head, int value) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = value;
newNode->next = *head;
*head = newNode;
}
void display(struct Node* head) {
struct Node* current = head;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
int main() {
struct Node* head = NULL;
insert(&head, 10);
insert(&head, 20);
insert(&head, 30);
insert(&head, 40);
printf("Linked List: ");
display(head);
return 0;
}
Output:
Linked List: 40 30 20 10
Explanation:
- The program defines a structure called
Node
that represents a node in the linked list. Each node contains an integerdata
and a pointernext
to the next node. - The
insert
function is used to insert a new node at the beginning of the linked list. It allocates memory for a new node, sets its data value, and updates thenext
pointer to point to the current head of the list. Finally, it updates the head pointer to point to the new node. - The
display
function is used to traverse the linked list and print the data values of each node. - In the
main
function, a pointerhead
is initialized asNULL
to represent an empty linked list. - Several nodes are inserted into the list using the
insert
function. - The linked list is displayed using the
display
function.
Rules to follow while using Pointers in C:
- Initialize Pointers: Always initialize pointers before using them. Assign them a valid memory address, either by pointing to an existing variable or using memory allocation functions like
malloc
orcalloc
. - Avoid Dereferencing Null Pointers: Null pointers do not point to valid memory addresses. Avoid dereferencing null pointers, as it can lead to crashes or undefined behavior.
- Avoid Dereferencing Uninitialized Pointers: Dereferencing uninitialized pointers can also result in crashes or unpredictable behavior. Always initialize pointers before accessing the memory they point to.
- Match Pointer Type with the Variable Type: Ensure that the pointer type matches the type of the variable it is pointing to. Pointers have a specific type associated with them, which determines the size of the memory block they can access.
- Respect Memory Allocation and Deallocation: When using dynamic memory allocation functions like
malloc
,calloc
, orrealloc
, make sure to deallocate the memory usingfree
when it is no longer needed. Failing to do so can result in memory leaks. - Avoid Pointer Arithmetic Errors: Be cautious when performing pointer arithmetic. Adding or subtracting an integer to a pointer should be done carefully to ensure that it remains within the allocated memory range.
- Be Mindful of Lifetime and Scope: Pointers should not be used to access memory outside their valid lifetime or scope. Avoid accessing memory that has already been deallocated or memory belonging to other variables.
- Avoid Pointer Aliasing: Pointer aliasing refers to multiple pointers pointing to the same memory location. It can lead to unexpected behavior and should be avoided unless explicitly intended.