Pointers in C

Pointers in C. Pointers in C Tutorial. Learn Pointers in C
Pointers in C

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 type int * (integer pointer) and assign it the address of num using the & operator.
  • The first printf statement displays the value of num.
  • The second printf statement displays the value stored in ptr, which is the memory address of num.
  • The third printf statement uses the * operator to access the value pointed by ptr, which is the value of num.

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 type int * and assign it the address of the first element of arr.
  • The first printf statement uses the * operator to access the value of the first element of arr using the pointer ptr.
  • The second and third printf statements demonstrate how to access the second and third elements of arr 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 pointer ptr using the syntax void (*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 pointer ptr.
  • The printf statement demonstrates how to call the function indirectly through the function pointer ptr.

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) and age (an integer).
  • In the main function, we declare a structure variable person and a pointer to the structure ptr.
  • We assign the address of the person structure to the pointer ptr using the & operator.
  • The strcpy function is used to copy the string “John” to the name 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 variables x and y and display their initial values.
  • We call the swap function, passing the addresses of x and y.
  • After the swap, the values of x and y 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 integer data and a pointer next 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 the next 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 pointer head is initialized as NULL 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:

  1. 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 or calloc.
  2. 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.
  3. 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.
  4. 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.
  5. Respect Memory Allocation and Deallocation: When using dynamic memory allocation functions like malloc, calloc, or realloc, make sure to deallocate the memory using free when it is no longer needed. Failing to do so can result in memory leaks.
  6. 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.
  7. 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.
  8. 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.

More: