Sitemap
Musheerk

Musheerk

Pointers in C

7 min readSep 18, 2024

--

A Comprehensive Guide

What Are Pointers?

In C programming, pointers are variables that store the memory address of another variable. They are one of the most powerful features of C because they allow you to work directly with memory, offering flexibility and efficiency in memory manipulation.

Why Use Pointers?

  1. Efficiency: Pointers allow you to directly access and manipulate memory, which can lead to faster and more efficient programs.
  2. Dynamic Memory Allocation: Pointers are essential when dealing with functions like malloc() and calloc(), which allocate memory at runtime.
  3. Function Arguments: Pointers allow passing references to variables, enabling call by reference and modifying variables within functions.
  4. Data Structures: Pointers are crucial in implementing complex data structures like linked lists, trees, and graphs.

Basics of Pointers

Declaring a Pointer:

int *ptr;  // Declares a pointer to an integer

Pointer Initialization: You can initialize a pointer by storing the address of a variable using the address-of operator (&).

int x = 10; int *ptr = &x;  // ptr stores the address of x

Dereferencing a Pointer: The dereference operator (*) accesses the value at the memory address stored by the pointer.

printf("%d", *ptr);  // Outputs 10 (value of x)

Types of Pointers in C

In C, there are several types of pointers, each serving a specific purpose. Here’s a breakdown of the most important ones:

1. Null Pointer

  • A null pointer is a pointer that doesn’t point to any valid memory location. It is often used to signify that a pointer is not yet assigned.
int *p = NULL;
if (p == NULL) {
printf("Pointer is null.\n");
}

2. Void Pointer (Generic Pointer)

  • A void pointer can point to any data type, but it must be typecasted to a specific type before dereferencing.
void *ptr;
int a = 5;
ptr = &a; // Can point to any type of data
printf("%d\n", *(int*)ptr); // Typecast to int* before dereferencing

3. Dangling Pointer

  • A dangling pointer is a pointer that points to memory that has been freed or deallocated. It can cause undefined behavior if dereferenced.
int *p = (int*) malloc(sizeof(int));
*p = 10;
free(p); // Memory is deallocated
// Now p is a dangling pointer

4. Wild Pointer

  • A wild pointer is an uninitialized pointer, which means it points to a random memory location. Using a wild pointer can lead to unpredictable behavior.
int *p;  // Uninitialized pointer
*p = 10; // This can cause a crash or unexpected behavior

5. Constant Pointer

  • A constant pointer is a pointer that cannot change the address it’s pointing to, though the value at that address may change.
int x = 10;
int *const p = &x; // Constant pointer
*p = 20; // Allowed, value at p can change
// p = &y; // Not allowed, p can't change the address it holds

6. Pointer to Constant

  • A pointer to constant means that the value being pointed to cannot be modified, though the pointer itself can point to another address.
const int x = 10;
const int *p = &x; // Pointer to constant
// *p = 20; // Not allowed, value can't be modified
p = &y; // Allowed, pointer can change the address

7. Function Pointer

  • Function pointers can be used to implement the strategy pattern, where the behavior of a program changes at runtime based on a specific strategy (or function) selected.
#include <stdio.h>

// Different strategies
void addition(int a, int b) {
printf("Sum: %d\n", a + b);
}

void subtraction(int a, int b) {
printf("Difference: %d\n", a - b);
}

// Function that accepts different strategies via function pointer
void executeOperation(void (*operation)(int, int), int a, int b) {
operation(a, b); // Call the chosen operation (addition or subtraction)
}

int main() {
void (*op)(int, int);

// Choose addition strategy
op = addition;
executeOperation(op, 10, 5); // Outputs: Sum: 15

// Choose subtraction strategy
op = subtraction;
executeOperation(op, 10, 5); // Outputs: Difference: 5

return 0;
}

8. Pointer to Pointer (Double Pointer)

  • A pointer to pointer is a pointer that holds the address of another pointer. It’s often used in multi-dimensional arrays or when manipulating dynamically allocated memory.
int x = 10;
int *p = &x; // Pointer to int
int **pp = &p; // Pointer to pointer to int
printf("%d", **pp); // Outputs 10

Working with Pointers Examples(call by refrence)

Here’s a simple example showing how pointers can be used to modify the value of a variable inside a function:

#include <stdio.h>
void modify(int *p) {
*p = 20; // Modify the value of the variable at the address stored in p
}
int main() {
int x = 10;
printf("Before: %d\n", x); // Outputs 10
modify(&x); // Pass the address of x to the function
printf("After: %d\n", x); // Outputs 20
return 0;
}

The modify() function takes a pointer to an integer and modifies the value stored at that address.

Advanced Pointer Concepts

1. Pointer Arithmetic

Pointers support arithmetic operations like increment (++) and decrement (--). These operations are performed based on the size of the data type the pointer is pointing to.

  • Incrementing a pointer:
    When you increment a pointer, it moves to the next memory location based on the size of the data type.
int arr[] = {10, 20, 30, 40};  //in arr inc & decr is not work then we use pointer
int *ptr = arr;
ptr++; // Moves the pointer to the next integer (address of arr[1])
printf("%d", *ptr); // Outputs 20

2. Array and Pointers

In C, arrays and pointers are closely related. The name of an array is a pointer to its first element.

#include <stdio.h>
int main() {
int arr[] = {1, 2, 3, 4};
int *ptr = arr; // Pointer points to the first element of arr
for (int i = 0; i < 4; i++) {
printf("%d ", *(ptr + i)); // Access array elements using pointer arithmetic
}
return 0;
}

3. Pointer to Pointer

A pointer to a pointer stores the address of another pointer.

#include <stdio.h>
int main() {
int x = 10;
int *ptr = &x; // Pointer to int
int **pptr = &ptr; // Pointer to pointer to int
printf("%d", **pptr); // Outputs 10
return 0;
}

Pointer Best Practices

Always initialize pointers with NULL:
A pointer that is not initialized will point to random memory (garbage values), leading to undefined behavior.

int *p = NULL;  // It's good practice to initialize a pointer to NULL.

Check for NULL before dereferencing:
Dereferencing a NULL pointer will lead to a crash or segmentation fault.

Get Musheerk’s stories in your inbox

Join Medium for free to get updates from this writer.

Avoid memory leaks:
When using dynamic memory allocation, always free memory after use to prevent memory leaks.

Interview Questions on Pointers

What is a pointer?

  • A pointer is a variable that stores the memory address of another variable.

What’s the difference between * and & operators?

  • & gives the address of a variable, while * dereferences a pointer, accessing the value stored at the memory address.

Explain the difference between a pointer to a var and a pointer to an arr.

  • A pointer to a variable stores the address of a single variable, while a pointer to an array stores the address of the first element of the array.

How do you pass a pointer to a function? Why is it useful?

  • You can pass a pointer to a function by using the variable’s address (&). This is useful for modifying the original value of a variable, rather than working with a copy.

What is a dangling pointer?

  • A dangling pointer is a pointer that points to a memory location that has been freed or deallocated.

What’s the difference between a constant pointer and a pointer to a constant?

  • A constant pointer cannot change the address it points to, but the value at that address can change. A pointer to a constant cannot modify the value at the address, but it can point to a different address.

Example Problem: Removing all Substrings Using Pointers

Let’s explore a practical use case where we manipulate a string using pointers. We’ll remove a substring from a given string.

#include <stdio.h>
#include <string.h>
int main() {
char str[] = "this is vectoris indiansis";
char subStr[] = "is";
int l = strlen(subStr);
char *pos;
while ((pos = strstr(str, subStr)) != NULL) {
int i = pos - str; // Find the position of the substring
for (int j = i; str[j]; j++) {
str[j] = str[j + l]; // Shift the remaining string
}
}
printf("Resultant string: %s\n", str);
return 0;
}

Explanation:

  • The program removes all occurrences of the substring "is" from the string.
  • We use strstr() to find the position of the substring, and pointer arithmetic (pos - str) to calculate the index.
  • Once we locate the substring, we shift the remaining part of the string left to overwrite it.

Memory and Pointers: Common Mistakes

  1. Uninitialized Pointers:
    Accessing an uninitialized pointer can cause a program crash.
  2. Dangling Pointers:
    After free()-ing a pointer, don’t use it until it’s re-initialized.
  3. Buffer Overflow:
    When working with arrays and pointers, ensure you don’t write past the allocated memory block.
  4. Not freeing dynamically allocated memory:
    Always free memory allocated by malloc(), calloc(), or realloc().

Conclusion

Pointers are a powerful tool in C, providing direct memory access and flexibility in handling data structures. However, with this power comes responsibility. Proper use of pointers requires a good understanding of memory management, pointer arithmetic, and best practices to avoid pitfalls like memory leaks and crashes.

By mastering pointers, you’ll have a deeper understanding of how memory works in C and be prepared for advanced topics like dynamic memory allocation, data structures. click here

https://medium.com/@musheerk799/understanding-dynamic-memory-allocation-in-c-39e9b2544f4b

--

--