6.1 - Working with pointers

6.1 - Working with pointers

Pointers are one of the most powerful and essential features. They allow direct manipulation of memory, which is critical for system-level programming, resource management, and optimizing performance. However, pointers can also be tricky and prone to errors if not used carefully.

What is a Pointer?

A pointer is a variable that stores the memory address of another variable. Pointers allow us to access and modify variables indirectly. Instead of storing a value directly, a pointer stores the location of the value in memory.

Basic Syntax

  • To declare a pointer:
  •   int *ptr;
    

This declares ptr as a pointer to an int. It can store the address of an integer variable.

  • To store the address of a variable into a pointer:
    int a = 10;
    ptr = &a;  // Store the address of 'a' in 'ptr'
    
  • To access the value stored at the address a pointer is pointing to, we use dereferencing:
    printf("%d", *ptr);  // Prints the value of 'a', which is 10
    

Pointer Types

Pointers can point to different types of data. The type of pointer determines the size and type of data it can point to.

Common Pointer Types

  • Pointer to int:

    int *p;
    
  • Pointer to float:

    float *p;
    
  • Pointer to char:

    char *p;
    

Each of these pointers can only store addresses of the corresponding data types.

Pointer Arithmetic

Pointers in C support arithmetic operations like addition and subtraction. However, the arithmetic operations are scaled based on the size of the data type the pointer is pointing to.

Example:

If ptr is a pointer to int and int is 4 bytes:

int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;

ptr++;  // Now ptr points to the second element in the array (arr[1])

When we increment the pointer (ptr++), the address it holds is increased by sizeof(int) bytes (typically 4 bytes on most platforms).

Arithmetic Operations:

  • Increment (ptr++): Moves the pointer to the next memory location based on the size of the data type it points to.
  • Decrement (ptr–): Moves the pointer to the previous memory location.
  • Addition (ptr + n): Moves the pointer n elements forward.
  • Subtraction (ptr - n): Moves the pointer n elements backward.

Pointers and Arrays

In C, arrays and pointers are closely related. The name of an array is a constant pointer to its first element. Thus, an array can be traversed using a pointer.

Example:

int arr[] = {10, 20, 30, 40};
int *ptr = arr;

for (int i = 0; i < 4; i++) {
    printf("%d ", *(ptr + i));  // Access each element using pointer arithmetic
}

Important Notes:

  • arr[i] is equivalent to *(arr + i).
  • An array name (arr) is not modifiable (i.e., you cannot do arr++).

Pointer to Pointer (Double Pointer)

A pointer to pointer is a pointer that stores the address of another pointer. This is useful when dealing with multi-dimensional arrays or when passing a pointer to a function that modifies the original pointer.

Example:

int a = 10;
int *ptr = &a;
int **pptr = &ptr;  // Pointer to pointer

printf("Value of a: %d", **pptr);  // Dereference twice to get the value of 'a'

Here, pptr stores the address of ptr, and *pptr gives ptr, and **pptr gives the value of a.

Pointers and Functions

Pointers can be passed to functions to allow them to modify the original variable. This is particularly useful for passing large data structures (like arrays) efficiently or modifying values in the calling function.

Passing Pointers to Functions:

void increment(int *num) {
    (*num)++;  // Dereference and modify the value
}

int main() {
    int a = 10;
    increment(&a);  // Pass the address of 'a'
    printf("%d", a);  // 'a' is now 11
}

Here, increment modifies the value of a directly via the pointer.

Returning a Pointer from a Function:

Be cautious when returning pointers from functions. Pointers to local variables should never be returned because local variables get destroyed when the function ends, and the returned pointer will point to invalid memory.

Dynamic Memory Allocation

Dynamic memory allocation allows us to allocate memory at runtime using pointers. In C, functions like malloc(), calloc(), realloc(), and free() are used for this purpose.

Example:

int *ptr = (int *)malloc(5 * sizeof(int));  // Allocate memory for 5 integers

if (ptr == NULL) {
    printf("Memory allocation failed!");
} else {
    for (int i = 0; i < 5; i++) {
        ptr[i] = i * 10;  // Store values in the allocated memory
    }

    free(ptr);  // Deallocate memory after use
}
  • malloc(): Allocates memory without initializing.
  • calloc(): Allocates and initializes memory to zero.
  • realloc(): Resizes previously allocated memory.
  • free(): Frees dynamically allocated memory.

Null Pointers and Pointer Safety

A null pointer is a pointer that doesn’t point to any valid memory location. It’s usually initialized to NULL to indicate that the pointer is not assigned a valid address.

Example:

int *ptr = NULL;  // Null pointer

if (ptr == NULL) {
    printf("Pointer is not pointing to any valid memory.");
}

Dereferencing a null pointer results in undefined behavior and should be avoided.

Pointer Safety:

  • Always initialize pointers: Uninitialized pointers (also called wild pointers) may contain garbage values, leading to unpredictable behavior.
  • Check for null pointers before dereferencing them.
  • Avoid dangling pointers: A pointer that points to memory that has been freed (using free() or out-of-scope variables) is called a dangling pointer.

Void Pointers (Generic Pointers)

A void pointer (also called a generic pointer) is a special type of pointer that can hold the address of any data type.

Example:

int a = 10;
void *ptr = &a;  // 'ptr' can store the address of any type of variable

// To dereference, you must cast it to the correct data type:
printf("%d", *(int *)ptr);  // Cast void pointer to 'int *' before dereferencing

Void pointers are commonly used in generic programming and dynamic memory allocation functions like malloc() and free().

Const Pointers

You can declare pointers as const to prevent modification of either the pointer itself or the value it points to.

Examples:

  • Pointer to a const value: You cannot modify the value through this pointer.
    const int *ptr;
    
  • Constant pointer: You cannot modify the pointer itself, but you can modify the value it points to.
    int *const ptr;
    
  • Constant pointer to constant data: Neither the pointer nor the value it points to can be modified.
    const int *const ptr;
    

Summary

Pointers are a powerful feature, providing flexibility in memory management, efficient data handling, and control over system resources. However, they require careful handling to avoid errors like segmentation faults, memory leaks, and undefined behavior.

By understanding how to declare, initialize, manipulate, and deallocate pointers properly, you can harness their full potential, especially when working with dynamic memory, arrays, and functions.



© 2025 Easy and fast Programming guide For C

Powered by Tessera for Jekyll