Login

Sign Up

Pointer in C
Tushar Varshney

Posted on Jan 24, 2025 | Coding

Pointer in C

Hey Coders! Today, we are going to dive deeper into the concept of pointers in C. We'll explore various aspects of pointers, including their definition and declaration, how to initialize and use them, pointer arithmetic for performing operations on pointer values, the relationship between arrays and pointers, function pointers for advanced usage, and the dereferencing operator to access the value stored at the pointer's address.


List of content:

  • Defintion.
  • Need of Pointers.
  • Decleration and Initialization.
  • Types of Pointer.
  • Size of Pointer.
  • Pointer Arithmetic.
  • Advantages and Disadvantages.

Let's start.....


Definition:

In C programming, a pointer is a variable that stores the memory address of another variable. Instead of holding a value directly, a pointer holds the location where a value is stored, allowing for powerful operations such as dynamic memory allocation, array manipulation, and function pointers.

Take a example:

int x = 10;    // Regular integer variable
int *p = &x;   // Pointer variable 'p' stores the address of 'x'

// Accessing the value at the address stored in 'p'
printf("Value of x: %d\n", *p);   // Output: Value of x: 10


Need of Pointer:

Pointers are a fundamental and powerful feature in C programming, and their use brings several advantages:

  • Dynamic Memory Management: Pointers enable the allocation and deallocation of memory at runtime using functions like malloc, calloc, realloc, and free. This is essential for creating dynamic data structures like linked lists, trees, and graphs.
  • Efficient Array Handling: Pointers provide a way to iterate through arrays more efficiently. Instead of using array indices, you can use pointer arithmetic to navigate through array elements, which can result in more concise and often faster code.
  • Pass-By-Reference: In C, functions can be called by passing the address of a variable, allowing the function to modify the original variable. This is useful for returning multiple values from a function and for passing large structures efficiently without copying.
  • Implementing Data Structures: Complex data structures such as linked lists, stacks, queues, and trees rely heavily on pointers. Pointers allow these structures to be dynamic and flexible, growing and shrinking as needed.
  • Function Pointers: Pointers can be used to store the address of functions, allowing for dynamic function calls and the implementation of callback functions. This is particularly useful in situations where the function to be called is determined at runtime.
  • Hardware and System-Level Programming: Pointers allow direct access to memory and hardware registers, making them indispensable for system-level programming, such as writing device drivers, operating systems, and embedded systems.
  • Improved Performance: Using pointers can lead to more efficient and faster code by minimizing the overhead associated with copying large amounts of data.

Declaration and Initialization:

Declaration of Pointer:

Pointers are declared using the * operator.

int *p;  // Declaration of an integer pointer

Pointer Initialization:

Pointers are typically initialized using the address-of operator (&), which returns the address of a variable.

int x = 10;
int *p = &x;  // 'p' now stores the address of 'x'

Example:

#include <stdio.h>

int main() {
    int x = 10;
    int *p = &x;
    
    printf("Address of x: %p\n", p);
    printf("Value of x: %d\n", *p);
    
    return 0;
}

Output:

Address of x: 0x7ffee16a868c
Value of x: 10

Types of Pointer:

1. Null Pointer:

A null pointer is a pointer that doesn't point to any memory location. It's typically used for initialization and error handling.

#include <stdio.h>

int main() {
    int *ptr = NULL;

    if (ptr == NULL) {
        printf("Pointer is NULL.\n");
    } else {
        printf("Pointer is not NULL.\n");
    }

    return 0;
}

Output:

Pointer is NULL.

2. Void Pointer:

A void pointer is a generic pointer that can point to any data type. It must be cast to the appropriate type before dereferencing.

#include <stdio.h>

int main() {
    int x = 10;
    void *ptr = &x;

    printf("Value of x: %d\n", *(int *)ptr);

    return 0;
}

Output:

Value of x: 10

3. Wild Pointer:

A wild pointer is an uninitialized pointer that points to a random memory location. It should be avoided as it can lead to undefined behavior.

#include <stdio.h>

int main() {
    int *ptr;  // Wild pointer

    // To avoid being a wild pointer, initialize it:
    ptr = NULL;

    if (ptr != NULL) {
        printf("Pointer is initialized.\n");
    } else {
        printf("Pointer is not initialized.\n");
    }

    return 0;
}

Output:

Pointer is not initialized.

4. Dangling Pointer:

A dangling pointer points to a memory location that has been freed or deallocated. Using such a pointer can lead to undefined behavior.

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *ptr = (int *)malloc(sizeof(int));
    *ptr = 10;

    free(ptr);
    ptr = NULL;  // Avoid dangling pointer

    if (ptr == NULL) {
        printf("Pointer is NULL.\n");
    } else {
        printf("Pointer value: %d\n", *ptr);
    }

    return 0;
}

Output:

Pointer is NULL.

5. Function Pointer:

Function pointers store the address of a function, allowing dynamic function calls.

#include <stdio.h>

void sayHello() {
    printf("Hello, World!\n");
}

int main() {
    void (*funcPtr)() = sayHello;

    funcPtr();  // Call the function through the pointer

    return 0;
}

Output:

Hello, World!

6. Array of Pointers:

An array of pointers is an array where each element is a pointer to an object of the same type.

#include <stdio.h>

int main() {
    int x = 10, y = 20, z = 30;
    int *arr[3] = {&x, &y, &z};

    for (int i = 0; i < 3; i++) {
        printf("Value of element %d: %d\n", i, *arr[i]);
    }

    return 0;
}

Output:

Value of element 0: 10
Value of element 1: 20
Value of element 2: 30

7. Integer Pointer:

An integer pointer stores the address of an integer variable.

#include <stdio.h>

int main() {
    int x = 10;
    int *ptr = &x;

    printf("Value of x: %d\n", *ptr);

    return 0;
}

Output:

Value of x: 10

8. Structure Pointer:

A structure pointer stores the address of a structure variable.

#include <stdio.h>

struct Point {
    int x, y;
};

int main() {
    struct Point p = {10, 20};
    struct Point *ptr = &p;

    printf("Point coordinates: (%d, %d)\n", ptr->x, ptr->y);

    return 0;
}

Output:

Point coordinates: (10, 20)

9. Double Pointer:

A double pointer (pointer to pointer) stores the address of another pointer.

#include <stdio.h>

int main() {
    int x = 10;
    int *ptr1 = &x;
    int **ptr2 = &ptr1;

    printf("Value of x: %d\n", **ptr2);

    return 0;
}

Output:

Value of x: 10

10. Constant Pointer:

A constant pointer is a pointer whose address cannot be changed after initialization. It always points to the same address, but the value at that address can be modified.

#include <stdio.h>

int main() {
    int x = 10;
    int y = 20;
    int *const ptr = &x;

    printf("Value of x: %d\n", *ptr);
    *ptr = 15;  // Modifying the value at the address
    printf("New value of x: %d\n", *ptr);

    // Uncommenting the next line will cause a compilation error
    // ptr = &y;  // Error: assignment of read-only variable 'ptr'

    return 0;
}

Output:

Value of x: 10
New value of x: 15

11. Pointer to Constant:

A pointer to a constant is a pointer that points to a constant value. The pointer's address can be changed, but the value at the address it points to cannot be modified through the pointer.

#include <stdio.h>

int main() {
    int x = 10;
    int y = 20;
    const int *ptr = &x;

    printf("Value of x: %d\n", *ptr);

    // Uncommenting the next line will cause a compilation error
    // *ptr = 15;  // Error: assignment of read-only location '*ptr'

    ptr = &y;  // Changing the pointer address
    printf("Value of y: %d\n", *ptr);

    return 0;
}

Output:

Value of x: 10
Value of y: 20

Other types of Pointer in C:

1. Near Pointer:

Near pointers are used in DOS-based systems and point to a memory location within the first 64 KB segment. They are 16-bit pointers and are specific to certain architectures.

#include <stdio.h>

int main() {
    char *near_ptr; // Near pointer (specific to DOS-based systems)

    // Code demonstrating the use of near pointers is typically specific to old DOS environments

    return 0;
}

2. Far Pointer:

Far pointers are 32-bit pointers that can access memory outside the 64 KB segment in DOS-based systems. They consist of a 16-bit segment and a 16-bit offset.

#include <stdio.h>

int main() {
    char far *far_ptr; // Far pointer (specific to DOS-based systems)

    // Code demonstrating the use of far pointers is typically specific to old DOS environments

    return 0;
}

3. Huge Pointer:

Huge pointers are similar to far pointers but provide normalized access to memory beyond the 64 KB segment, ensuring that pointers are adjusted to a unique segment:offset pair.

#include <stdio.h>

int main() {
    char huge *huge_ptr; // Huge pointer (specific to DOS-based systems)

    // Code demonstrating the use of huge pointers is typically specific to old DOS environments

    return 0;
}

4. Generic Pointer:

A generic pointer is a pointer that does not have a specific type associated with it. In C, a void pointer (void *) is considered a generic pointer since it can point to any data type.

#include <stdio.h>

int main() {
    int x = 10;
    float y = 5.5;
    void *gen_ptr;

    gen_ptr = &x;
    printf("Value of x: %d\n", *(int *)gen_ptr);

    gen_ptr = &y;
    printf("Value of y: %.1f\n", *(float *)gen_ptr);

    return 0;
}

Output:

Value of x: 10
Value of y: 5.5

5. File Pointer:

File pointers are used to handle and manage file operations. They point to a structure that contains information about the file being accessed.

#include <stdio.h>

int main() {
    FILE *file_ptr;
    file_ptr = fopen("example.txt", "r");

    if (file_ptr == NULL) {
        printf("File could not be opened.\n");
    } else {
        printf("File opened successfully.\n");
        fclose(file_ptr);
    }

    return 0;
}

Output:

File opened successfully.

6. Normalized Pointer:

Normalized pointers ensure that the segment and offset pair are unique and point to the same memory location. This is particularly useful in segmented memory architectures. Here's an illustrative example:

#include <stdio.h>

int main() {
    char normalized *norm_ptr; // Normalized pointer (specific to DOS-based systems)

    // Typical use is for educational purposes on old DOS environments

    return 0;
}


Size of Pointer in C:

In C, the size of a pointer is determined by the architecture and the compiler of the system you're working on. Typically, the size of a pointer is the same regardless of the type of data it points to. Here are some key points regarding the size of pointers:

1. Architecture Dependence:

  • 32-bit Systems: On a 32-bit architecture, the size of a pointer is usually 4 bytes.
  • 64-bit Systems: On a 64-bit architecture, the size of a pointer is usually 8 bytes.

2. Type Independence:

The size of a pointer does not depend on the type of data it points to. Whether it points to an int, char, float, struct, or any other data type, the size remains the same for a given architecture.

3. Using the sizeof Operator:

You can use the sizeof operator to determine the size of a pointer. Here's an example demonstrating this:

#include <stdio.h>

int main() {
    int *int_ptr;
    char *char_ptr;
    float *float_ptr;
    double *double_ptr;
    struct Point {
        int x, y;
    } *struct_ptr;

    printf("Size of int pointer: %zu bytes\n", sizeof(int_ptr));
    printf("Size of char pointer: %zu bytes\n", sizeof(char_ptr));
    printf("Size of float pointer: %zu bytes\n", sizeof(float_ptr));
    printf("Size of double pointer: %zu bytes\n", sizeof(double_ptr));
    printf("Size of struct pointer: %zu bytes\n", sizeof(struct_ptr));

    return 0;
}

Output(on 64-bit system):

Size of int pointer: 8 bytes
Size of char pointer: 8 bytes
Size of float pointer: 8 bytes
Size of double pointer: 8 bytes
Size of struct pointer: 8 bytes

Output(on 32-bit system):

Size of int pointer: 4 bytes
Size of char pointer: 4 bytes
Size of float pointer: 4 bytes
Size of double pointer: 4 bytes
Size of struct pointer: 4 bytes


Pointer Arithmetic:

Pointer arithmetic is a powerful feature in C that allows you to perform arithmetic operations on pointers. This is particularly useful when working with arrays and dynamic memory. Here are the main operations you can perform with pointers:

  • Increment (++)
  • Decrement (--)
  • Addition (+)
  • Subtraction (-)
  • Comparison (==, !=, <, >, <=, >=)

1. Increment (++):

Incrementing a pointer advances it to the next memory location based on the type it points to.

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr = arr;

    printf("Initial pointer points to: %d\n", *ptr);
    ptr++; // Move to the next integer
    printf("After increment, pointer points to: %d\n", *ptr);

    return 0;
}

Output:

Initial pointer points to: 10
After increment, pointer points to: 20

2. Decrement (--):

Decrementing a pointer moves it to the previous memory location based on the type it points to.

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr = arr + 4; // Point to the last element

    printf("Initial pointer points to: %d\n", *ptr);
    ptr--; // Move to the previous integer
    printf("After decrement, pointer points to: %d\n", *ptr);

    return 0;
}

Output:

Initial pointer points to: 50
After decrement, pointer points to: 40

3. Addition (+):

Adding an integer to a pointer advances it by that many elements.

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr = arr;

    printf("Initial pointer points to: %d\n", *ptr);
    ptr = ptr + 3; // Move forward by 3 integers
    printf("After adding 3, pointer points to: %d\n", *ptr);

    return 0;
}

Output:

Initial pointer points to: 10
After adding 3, pointer points to: 40

4. Subtraction (-):

Subtracting an integer from a pointer moves it backward by that many elements.

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr = arr + 4; // Point to the last element

    printf("Initial pointer points to: %d\n", *ptr);
    ptr = ptr - 2; // Move backward by 2 integers
    printf("After subtracting 2, pointer points to: %d\n", *ptr);

    return 0;
}

Output:

Initial pointer points to: 50
After subtracting 2, pointer points to: 30

5.Comparison (==, !=, <, >, <=, >=):

You can compare two pointers to see if they point to the same memory location or if one points to a location before or after the other.

#include <stdio.h>

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int *ptr1 = arr;
    int *ptr2 = arr + 3;

    if (ptr1 < ptr2) {
        printf("ptr1 points to a location before ptr2\n");
    } else {
        printf("ptr1 does not point to a location before ptr2\n");
    }

    return 0;
}

Output:

ptr1 points to a location before ptr2


Advantages and Disadvantages:

Advantages and Disadvantages of Pointers in C

Advantages Disadvantages
Dynamic Memory: Efficient allocation. Complexity: Hard to read and maintain.
Array Handling: Efficient traversal. Memory Leaks: Risk if not freed properly.
Pass-by-Reference: Modifies original. Dangling Pointers: Undefined behavior.
Dynamic Structures: Implements complex structures. Null Pointer: Leads to runtime errors.
System Access: Low-level hardware access. Pointer Arithmetic: Error-prone.
Function Pointers: Enables dynamic calls. Security Risks: Vulnerable to attacks.
Performance: Reduces data copy overhead. Hard to Debug: Makes debugging tough.

For more topics click here.

Happy Coding!

4 Reactions

0 Bookmarks

Read next

Tushar Varshney

Tushar Varshney

Jan 6, 25

5 min read

|

Problem on Bubble Sort

Tushar Varshney

Tushar Varshney

Jan 8, 25

6 min read

|

Problem on Selection sort