Pointers

Definition

It is a derived data type that can store the address of other variables or a memory location. We can access and manipulate the data stored in that memory location using pointers.

A pointer is a variable which holds the (memory) address of other variables of the specified data type (like int,float,char), functions, or even other pointers.

For analogy, think about them like GPS coordinates to different things on earth - like: houses, trees, etc.

In programming we basically use pointers to store the other variable’s address.

A pointer variable is declared with a * before it.

Pass by value and Pass by reference

Pitfalls

Pointers are vulnerable to errors.

Accessing values through a pointer rather than accessing them directly isn’t free: indirection adds a small overhead.

  1. Pointers allow you to change values of what they point to
  2. Data races
    1. If we are using pointers, we are essentially allowing code to manipulate the memory something points to directly.
    2. In languages that facilitate concurrency, accessing the same bit of memory means that we are at risk of creating data-races.

They have following disadvantages:

  1. Memory corruption can occur if an incorrect value is provided to pointers.
  2. Pointers are a little bit complex to understand.
  3. Pointers are majorly responsible for memory leaks in C.
  4. Pointers are comparatively slower than variables in C.
  5. Uninitialized pointers might cause a segmentation fault.

Uses

  1. Allows low-level memory access, dynamic memory allocation, and other functionalities.
  2. Pass Arguments by Reference
  3. Accessing Array Elements
  4. Return Multiple Values from Function
  5. Dynamic Memory Allocation
  6. Implementing Data Structures
  7. In System-Level Programming where memory addresses are useful.
  8. In locating the exact value at some memory location.
  9. To avoid compiler confusion for the same variable name.
  10. To use in Control Tables.

Advantages

  1. Pointers are used for dynamic memory allocation and deallocation.
  2. An Array or a structure can be accessed efficiently with pointers
  3. Pointers are useful for accessing memory locations.
  4. Pointers are used to form complex data structures such as linked lists, graphs, trees, etc.
  5. Pointers reduce the length of the program and its execution time as well.

Size

As the pointers store the memory addresses, their size is independent of the type of data they are pointing to. This size of pointers only depends on the system architecture.

The size of the pointers in C is equal for every pointer type. The size of the pointer does not depend on the type it is pointing to. It only depends on the operating system and CPU architecture. The size of pointers in C is

  1. 8 bytes for a 64-bit System
  2. 4 bytes for a 32-bit System

Syntax

Types and Declarations

int *ptr; // Integer pointer

char *ptr = &array_name; // Array pointer

struct struct_name *ptr; // Structure pointer

int (*ptr) (int, char); // Function pointer. They point to functions.

datatype ** pointer_name; // Double pointers. Pointers that store the memory address of another pointer.

data_type *pointer_name = NULL; // Null Pointer. They don't point to any memory location.
pointer_name = NULL; // Null Pointer. They don't point to any memory location.

void * pointer_name; // Void pointer. Type is void. They don't have any associated data type.

int *ptr; // Wild pointers. Not initialized to anything yet.
char *str; // Wild pointers. Not initialized to anything yet.

data_type * const pointer_name; // Constant pointers. The memory address stored inside the pointer is constant and it cannot be modified once it is defined. They always point to the same memory address.

const data_type * pointer_name; // A pointer pointing to a constant value that cannot be modified.

Other types

  1. Far pointer
  2. Null pointer
    1. They don’t point to anything.
  3. Dangling pointer
    1. The address to which the pointer is pointing to is no longer accessible in the memory.
  4. Dangling pointer vs Null pointer
    1. Dangling pointers are like GPS coordinates that refer to a place where there used to be a house but the house got swept up by an earthquake (garbage collection). Null pointers never referred to any place.
  5. Huge pointer
  6. Complex pointer
  7. Near pointer
  8. Normalized pointer
  9. File pointer

Initialization

int var = 10;
int * ptr;
ptr = &var;

or

int *ptr = &var;

Dereferencing

Dereferencing a pointer is the process of accessing the value stored in the memory address specified in the pointer. We use the same * dereferencing operator that we use in the pointer declaration.

Examples

swap function with and without using pointers

package mynumbers

import "fmt"

func SwapNumbers_passing_values(a int, b int) {

        var temp int = a
        a = b
        b = temp

        fmt.Println("a inside the swap function: ", a)
        fmt.Println("b inside the swap function: ", b)
}

func SwapNumbers_passing_pointers(c *int, d *int) {

        var temp int = *c
        // Go to the address c is holding (GPS coordinates of c)
        // Now get the value at that address (the house)
        // Now assign the value (the house that used to be at c) to temp.

        *c = *d
        // When *d is assigned to *c
        // Go to the address d is holding (GPS coordinates of d)
        // Now get the value at that address (the house)
        // Now go to the address ‘c’ is holding (GPS coordinates of c)
        // Now assign the value at d (the house that used to be at d) to the c.

        *d = temp
        // When temp is assigned to *
        // Go to the address d is holding (GPS coordinates of d)
        // Now assign temp (the house) to d.

        fmt.Println("c inside the swap function: ", *c)
        fmt.Println("d inside the swap function: ", *d)
}

Test driver

func main() {
    // swap numbers
    var a int = 12
    var b int = 34

    fmt.Println("a before swapping: ", a) // 12
    fmt.Println("b before swapping: ", b) // 34
    mynumbers.SwapNumbers_passing_values(a, b)
    fmt.Println("a after swapping: ", a) // 12
    fmt.Println("b after swapping: ", b) // 34

    // The local variables of the function SwapNumbers_passing_values are changing but not ‘a’ and ‘b’.

    // swap numbers
    var c int = 56
    var d int = 78
    fmt.Println("c before swapping: ", c) // 56
    fmt.Println("d before swapping: ", d) // 78
    mynumbers.SwapNumbers_passing_pointers(&c, &d)
    fmt.Println("c after swapping: ", c) // 56
    fmt.Println("d after swapping: ", d) // 78}