Chapter 12: Pointers

📌 Learning Objectives
  • LO 12.1: Understand the concept of pointers and memory addressing
  • LO 12.2: Declare and initialize pointer variables
  • LO 12.3: Perform pointer arithmetic and understand scale factor
  • LO 12.4: Use pointers with arrays and strings
  • LO 12.5: Work with pointers to functions and structures
  • LO 12.6: Understand pointer compatibility and common pointer errors

12.1 Introduction to Pointers

A pointer is a derived data type in C that stores memory addresses as their values. Pointers contain the addresses of other variables, allowing indirect access to data stored in memory.

Memory organization:

        Address      Value
        +------+------+
        | 1000 |  179 |  ← variable quantity
        +------+------+
        | 1002 |  200 |
        +------+------+
        | 1004 |  150 |
        +------+------+
        | 5000 | 1000 |  ← pointer p pointing to quantity
        +------+------+

If int quantity = 179 at address 1000,
and int *p = &quantity at address 5000,
then:
    p  = 1000 (address of quantity)
    *p = 179  (value of quantity)

Benefits of pointers:

12.2 Understanding Pointers

Every variable in memory has an address. The address operator & gives the address of a variable.

📝 Worked-Out Problem 12.1

Accessing the address of a variable:

#include <stdio.h>

int main() {
    int a = 10;
    float b = 20.5;
    char c = 'X';
    
    printf("Value of a = %d, Address of a = %u\n", a, &a);
    printf("Value of b = %.2f, Address of b = %u\n", b, &b);
    printf("Value of c = %c, Address of c = %u\n", c, &c);
    
    return 0;
}
Output (typical):
Value of a = 10, Address of a = 3221224900
Value of b = 20.50, Address of b = 3221224896
Value of c = X, Address of c = 3221224895

Note: The actual addresses may vary each time the program runs.

12.3 Declaring Pointer Variables

Pointer variables are declared using the asterisk (*) operator:

data_type *pointer_name;

Examples:

int *p;      // pointer to integer
float *x;    // pointer to float
char *ch;    // pointer to character

12.4 Initialization of Pointer Variables

Pointers must be initialized before use. They can be initialized with the address of a variable or NULL.

int quantity = 179;
int *p = &quantity;      // Initialize at declaration

// Or separately
int *p;
p = &quantity;           // Assign address after declaration

// NULL pointer
int *ptr = NULL;         // Points to nothing
⚠️ Important: Using an uninitialized pointer can cause unpredictable program behavior or crashes. Always initialize pointers before use.

12.5 Accessing a Variable Through its Pointer

The indirection operator (*) gives the value at the address stored in a pointer.

📝 Worked-Out Problem 12.2

Accessing variables through pointers:

#include <stdio.h>

int main() {
    int x = 10;
    int *ptr;
    
    ptr = &x;              // ptr stores address of x
    
    printf("Value of x = %d\n", x);
    printf("Address of x = %u\n", &x);
    printf("Value of ptr = %u\n", ptr);
    printf("Value pointed by ptr = %d\n", *ptr);
    
    *ptr = 25;             // Change x through pointer
    printf("New value of x = %d\n", x);
    
    return 0;
}
Output:
Value of x = 10
Address of x = 3221224900
Value of ptr = 3221224900
Value pointed by ptr = 10
New value of x = 25

12.6 Chain of Pointers

Pointers can point to other pointers, creating a chain of indirection.

int x = 10;
int *p = &x;      // pointer to int
int **q = &p;     // pointer to pointer to int
int ***r = &q;    // pointer to pointer to pointer to int

printf("x = %d\n", x);
printf("*p = %d\n", *p);
printf("**q = %d\n", **q);
printf("***r = %d\n", ***r);
Chain of pointers:

    r (address 3000) ──→ q (address 2000) ──→ p (address 1000) ──→ x (value 10)
         |                      |                      |
         ↓                      ↓                      ↓
       value 2000             value 1000             value 10

12.7 Pointer Expressions and Arithmetic

Pointers can be used in expressions with certain arithmetic operations:

Invalid pointer operations:

12.8 Pointer Increments and Scale Factor

When a pointer is incremented, it points to the next element of its type. The increment is multiplied by the size of the data type (scale factor).

int *p = (int *)1000;  // Assuming int is 4 bytes
p++;  // p now points to address 1004 (1000 + 4)
Data TypeSize (bytes)Scale Factor
char11
int4 (typical)4
float44
double88

📝 Worked-Out Problem 12.3

Pointer increments and scale factor:

#include <stdio.h>

int main() {
    int i = 100;
    int *int_ptr = &i;
    
    float f = 3.14;
    float *float_ptr = &f;
    
    char c = 'A';
    char *char_ptr = &c;
    
    printf("int_ptr = %u\n", int_ptr);
    int_ptr++;
    printf("int_ptr after increment = %u\n", int_ptr);
    printf("Difference = %u\n\n", int_ptr - &i);
    
    printf("float_ptr = %u\n", float_ptr);
    float_ptr++;
    printf("float_ptr after increment = %u\n", float_ptr);
    printf("Difference = %u\n\n", float_ptr - &f);
    
    printf("char_ptr = %u\n", char_ptr);
    char_ptr++;
    printf("char_ptr after increment = %u\n", char_ptr);
    printf("Difference = %u\n", char_ptr - &c);
    
    return 0;
}
Output:
int_ptr = 3221224900
int_ptr after increment = 3221224904
Difference = 4

float_ptr = 3221224896
float_ptr after increment = 3221224900
Difference = 4

char_ptr = 3221224895
char_ptr after increment = 3221224896
Difference = 1

12.9 Pointers and Arrays

The name of an array is a constant pointer to the first element of the array.

int x[5] = {10, 20, 30, 40, 50};
int *p;

p = x;               // p points to x[0] (same as p = &x[0])
printf("%d", *p);    // prints 10

p++;                 // now p points to x[1]
printf("%d", *p);    // prints 20

Relationship between pointers and arrays:

p = &x[0];          // p points to first element
x[i] ≡ *(x + i)     // array indexing using pointer arithmetic
&x[i] ≡ (x + i)     // address of element i

📝 Worked-Out Problem 12.4

Accessing array elements using pointers:

#include <stdio.h>

int main() {
    int x[5] = {10, 20, 30, 40, 50};
    int *p, i;
    
    p = x;  // p points to x[0]
    
    printf("Using array indexing:\n");
    for (i = 0; i < 5; i++)
        printf("x[%d] = %d\t Address = %u\n", i, x[i], &x[i]);
    
    printf("\nUsing pointer arithmetic:\n");
    for (i = 0; i < 5; i++)
        printf("*(p+%d) = %d\t Address = %u\n", i, *(p+i), p+i);
    
    printf("\nUsing pointer increment:\n");
    p = x;
    for (i = 0; i < 5; i++) {
        printf("*p = %d\t Address = %u\n", *p, p);
        p++;
    }
    
    return 0;
}

12.10 Pointers and Two-Dimensional Arrays

For a two-dimensional array, we need to understand how elements are stored in row-major order.

int a[3][4] = {
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12}
};

The element a[i][j] can be accessed using pointer notation as:

*(*(a + i) + j)  // Equivalent to a[i][j]

📝 Worked-Out Problem 12.5

Accessing 2D array elements using pointers:

#include <stdio.h>

int main() {
    int a[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    int i, j;
    int *p;
    
    printf("Using array indexing:\n");
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 4; j++)
            printf("%4d", a[i][j]);
        printf("\n");
    }
    
    printf("\nUsing pointer arithmetic:\n");
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 4; j++)
            printf("%4d", *(*(a + i) + j));
        printf("\n");
    }
    
    printf("\nTreating 2D as 1D:\n");
    p = &a[0][0];  // pointer to first element
    for (i = 0; i < 12; i++)
        printf("%d ", *(p + i));
    printf("\n");
    
    return 0;
}

12.11 Pointers and Character Strings

Strings can be accessed using character pointers.

char *str = "Hello World";

📝 Worked-Out Problem 12.6

String handling using pointers:

#include <stdio.h>

int string_length(char *str) {
    int len = 0;
    while (*str != '\0') {
        len++;
        str++;
    }
    return len;
}

void string_copy(char *dest, char *src) {
    while (*src != '\0') {
        *dest = *src;
        dest++;
        src++;
    }
    *dest = '\0';
}

int main() {
    char *str1 = "Hello World";
    char str2[50];
    
    printf("String: %s\n", str1);
    printf("Length: %d\n", string_length(str1));
    
    string_copy(str2, str1);
    printf("Copied string: %s\n", str2);
    
    // Printing characters using pointer
    char *p = str1;
    printf("Characters: ");
    while (*p != '\0')
        printf("%c ", *p++);
    printf("\n");
    
    return 0;
}
Output:
String: Hello World
Length: 11
Copied string: Hello World
Characters: H e l l o W o r l d

12.12 Array of Pointers

We can create arrays that store pointers. This is particularly useful for handling multiple strings.

char *names[5] = {
    "John",
    "Alice",
    "Bob",
    "David",
    "Charlie"
};

This creates a "ragged array" where each string occupies only the space it needs, unlike a 2D character array which wastes space.

📝 Worked-Out Problem 12.7

Array of pointers to strings:

#include <stdio.h>

int main() {
    char *cities[5] = {
        "Mumbai",
        "Delhi",
        "Chennai",
        "Kolkata",
        "Bangalore"
    };
    
    int i;
    
    printf("List of cities:\n");
    for (i = 0; i < 5; i++)
        printf("%d. %s (address: %u)\n", i+1, cities[i], cities[i]);
    
    printf("\nAccessing characters in the first city:\n");
    char *p = cities[0];
    while (*p != '\0')
        printf("%c ", *p++);
    printf("\n");
    
    return 0;
}

12.13 Pointers as Function Arguments (Call by Reference)

Passing pointers to functions allows the function to modify the original variables in the calling function.

📝 Worked-Out Problem 12.8

Swapping values using pointers:

#include <stdio.h>

void swap(int *a, int *b) {
    int temp;
    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);  // Pass addresses
    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

📝 Worked-Out Problem 12.9

Function returning multiple values using pointers:

#include <stdio.h>

void math_ops(int a, int b, int *sum, int *diff, int *prod, float *quot) {
    *sum = a + b;
    *diff = a - b;
    *prod = a * b;
    if (b != 0)
        *quot = (float)a / b;
    else
        *quot = 0;
}

int main() {
    int x = 15, y = 4;
    int s, d, p;
    float q;
    
    math_ops(x, y, &s, &d, &p, &q);
    
    printf("x = %d, y = %d\n", x, y);
    printf("Sum = %d\n", s);
    printf("Difference = %d\n", d);
    printf("Product = %d\n", p);
    printf("Quotient = %.2f\n", q);
    
    return 0;
}
Output:
x = 15, y = 4
Sum = 19
Difference = 11
Product = 60
Quotient = 3.75

12.14 Functions Returning Pointers

A function can return a pointer, provided the memory it points to remains valid after the function returns.

int *larger(int *x, int *y) {
    if (*x > *y)
        return x;
    else
        return y;
}

int main() {
    int a = 10, b = 20;
    int *p = larger(&a, &b);
    printf("Larger value: %d\n", *p);
    return 0;
}
⚠️ Important: Never return a pointer to a local variable from a function. The local variable is destroyed when the function returns, leaving a dangling pointer.

12.15 Pointers to Functions

Like variables, functions also have addresses. We can create pointers to functions and use them to call functions indirectly.

return_type (*pointer_name)(parameter_list);

📝 Worked-Out Problem 12.10

Using pointers to functions:

#include <stdio.h>
#include <math.h>

double square(double x) {
    return x * x;
}

double cube(double x) {
    return x * x * x;
}

void table(double (*f)(double), double start, double end, double step) {
    double x;
    printf("x\tf(x)\n");
    for (x = start; x <= end; x += step) {
        printf("%.2f\t%.4f\n", x, (*f)(x));
    }
    printf("\n");
}

int main() {
    printf("Square function:\n");
    table(square, 1.0, 5.0, 1.0);
    
    printf("Cube function:\n");
    table(cube, 1.0, 5.0, 1.0);
    
    printf("Sine function:\n");
    table(sin, 0.0, 3.14, 1.0);
    
    return 0;
}
Partial Output:
Square function:
x f(x)
1.00 1.0000
2.00 4.0000
3.00 9.0000
4.00 16.0000
5.00 25.0000

12.16 Pointers and Structures

Pointers to structures are commonly used, especially when passing structures to functions.

struct student {
    int roll_no;
    char name[30];
    float marks;
};

struct student s1 = {101, "John", 85.5};
struct student *ptr = &s1;

// Access members using pointer
printf("Roll: %d\n", ptr->roll_no);     // using arrow operator
printf("Name: %s\n", ptr->name);        // ptr->member is equivalent to (*ptr).member

📝 Worked-Out Problem 12.11

Array of structures using pointers:

#include <stdio.h>

struct student {
    int roll_no;
    char name[30];
    float marks;
};

int main() {
    struct student s[3] = {
        {101, "John", 85.5},
        {102, "Alice", 92.0},
        {103, "Bob", 78.5}
    };
    
    struct student *ptr;
    int i;
    
    ptr = s;  // ptr points to first structure
    
    printf("Using array indexing:\n");
    for (i = 0; i < 3; i++)
        printf("%d\t%s\t%.2f\n", s[i].roll_no, s[i].name, s[i].marks);
    
    printf("\nUsing pointer:\n");
    for (i = 0; i < 3; i++) {
        printf("%d\t%s\t%.2f\n", (ptr+i)->roll_no, (ptr+i)->name, (ptr+i)->marks);
    }
    
    printf("\nUsing pointer increment:\n");
    ptr = s;
    for (i = 0; i < 3; i++) {
        printf("%d\t%s\t%.2f\n", ptr->roll_no, ptr->name, ptr->marks);
        ptr++;
    }
    
    return 0;
}

12.17 Pointer Compatibility and Casting

Pointers to different types are generally incompatible and cannot be assigned directly without casting.

int x = 10;
float *fp;
fp = &x;                    // Error: incompatible types
fp = (float *)&x;           // Allowed with cast, but dangerous

void pointer: A generic pointer that can point to any data type.

void *ptr;
int x = 10;
float f = 3.14;

ptr = &x;        // void pointer can point to int
ptr = &f;        // and also to float

// Must cast before dereferencing
printf("%d", *(int *)ptr);  // cast to appropriate type

12.18 Common Pointer Errors

⚠️ Common pointer mistakes:
  • Uninitialized pointers: Using a pointer before assigning a valid address
  • Forgetting & in scanf: scanf("%d", ptr); when ptr is uninitialized
  • Dangling pointers: Using a pointer after the memory it points to is freed
  • Memory leaks: Losing the address of dynamically allocated memory
  • Comparing incompatible pointers: Comparing pointers to different types
  • Incorrect pointer arithmetic: Not accounting for scale factor
  • Missing * in pointer declaration vs dereferencing confusion

Important Points to Remember

  • Only addresses of variables can be stored in pointer variables, not arbitrary values.
  • Do not store the address of a variable of one type into a pointer variable of another type without proper casting.
  • Pointer variables contain garbage until initialized; always initialize before use.
  • NULL (0) can be assigned to a pointer to indicate it points to nothing.
  • When an array is passed to a function, a pointer is actually passed (call by reference).
  • If a called function needs to modify a variable in the calling function, pass its address.
  • Be careful with operator precedence: *p++ increments the pointer, not the value pointed to.

Chapter Exercises

Review Questions

  1. State true or false:
    a) Pointer constants are the addresses of memory locations.
    b) The underlying type of a pointer variable is void.
    c) Pointer variables are declared using the address operator.
    d) It is possible to cast a pointer to float as a pointer to integer.
    e) Pointers to pointers can be used to describe pointers whose contents are the address of another pointer.
    f) A pointer can never be subtracted from another pointer.
    g) An integer can be added to a pointer.
    h) Pointers can be used as formal parameters in function headers.
    i) When an array is passed to a function, a pointer is passed.
  2. What is a pointer? How can it be initialized?
  3. Explain the effects of: int a, *b = &a;
  4. Distinguish between (*m)[5] and *m[5].
  5. What is printed by: printf("%u %u", &a, &a+1); if a is an integer array?

Multiple Choice Questions

  1. A pointer in C language is:
    a) address of some location
    b) useful to describe linked list
    c) can be used to access elements of an array
    d) All of the above
  2. Which constant values can be assigned to a pointer variable?
    a) 0
    b) NULL
    c) Neither
    d) Both 1 and 2
  3. Which of the following operations is allowed on a pointer variable?
    a) ptr+1
    b) ptr-1
    c) ptr++ and ptr--
    d) All of these
  4. Which of the following is incorrect about pointers?
    a) A pointer variable stores NULL until initialized.
    b) A pointer cannot be assigned any numeric constant except 0.
    c) Except for void pointer, assigning one pointer type to another without cast is error.
    d) The address operator (&) is not required when assigning array address to pointer.
  5. Given int x = 10, y = 10; int *p1 = &x, *p2 = &y;, what is (*p1)++?
    a) 10
    b) 11
    c) address of x
    d) None

Debugging Exercises

Find errors in the following code segments:

int *p;
*p = 25;  // Error?

int x = 10;
float *fp = &x;  // Error?

char *str = "Hello";
str[0] = 'h';  // Error?

int a[5] = {1,2,3,4,5};
int *p = a;
p = p + 1.5;  // Error?

int x = 10;
int *p = &x;
int **q = p;  // Error?

Programming Exercises

  1. Write a program using pointers to read an array of integers and print its elements in reverse order.
  2. Write a function to calculate the roots of a quadratic equation using pointer parameters to return the roots.
  3. Write a function that receives a sorted array and an integer, and inserts the value in its correct place using pointers.
  4. Write a function using pointers to add two matrices and return the result to the calling function.
  5. Write a function that receives a string and a character, and deletes all occurrences of that character using pointers.
  6. Write a function day_name that receives a number n and returns a pointer to the name of the corresponding day.
  7. Write a program to sort an array of names using pointers and function pointers for comparison.
  8. Write a function to search for a particular item using binary search, using pointers and pointer arithmetic.
  9. Write a function that reverses the elements of a given array using pointer parameters.
  10. Write a C program to demonstrate the scale factor by incrementing pointers of different types.

Interview Questions

  1. Which format specifiers are used to print the address of a pointer?
  2. What is a pointer to a pointer? Give an example.
  3. What will be the output of:
    char *ptr = "Hello";
    printf("%d %d", sizeof(*ptr), sizeof(ptr));
  4. What is a null pointer?
  5. What is a function pointer?
  6. What is a void pointer?
  7. What is the difference between an uninitialized pointer and a NULL pointer?
  8. What will be the output of:
    int arr[] = {10, 20, 30, 40, 50};
    int *p = arr;
    printf("%d %d", *p++, *p++);

Case Study: Processing Examination Marks

📝 Case Study: Rank list preparation using pointers

#include <stdio.h>
#include <string.h>
#define STUDENTS 5
#define SUBJECTS 3

struct student {
    char name[30];
    int marks[SUBJECTS];
    int total;
};

void calculate_totals(struct student *s, int n) {
    int i, j;
    for (i = 0; i < n; i++) {
        s[i].total = 0;
        for (j = 0; j < SUBJECTS; j++)
            s[i].total += s[i].marks[j];
    }
}

void sort_by_total(struct student *s, int n) {
    int i, j;
    struct student temp;
    
    for (i = 0; i < n-1; i++) {
        for (j = 0; j < n-i-1; j++) {
            if (s[j].total < s[j+1].total) {
                temp = s[j];
                s[j] = s[j+1];
                s[j+1] = temp;
            }
        }
    }
}

void print_rank_list(struct student *s, int n) {
    int i;
    printf("\nRank List:\n");
    printf("Rank\tName\t\tTotal\n");
    for (i = 0; i < n; i++)
        printf("%d\t%s\t\t%d\n", i+1, s[i].name, s[i].total);
}

int main() {
    struct student s[STUDENTS] = {
        {"John", {85, 90, 88}, 0},
        {"Alice", {92, 88, 95}, 0},
        {"Bob", {78, 82, 80}, 0},
        {"David", {88, 85, 90}, 0},
        {"Emma", {95, 92, 98}, 0}
    };
    
    calculate_totals(s, STUDENTS);
    sort_by_total(s, STUDENTS);
    print_rank_list(s, STUDENTS);
    
    return 0;
}
Output:
Rank List:
Rank Name Total
1 Emma 285
2 Alice 275
3 John 263
4 David 263
5 Bob 240

Case Study 2: Inventory Management

📝 Case Study: Inventory updating with structure pointers

#include <stdio.h>

struct stores {
    char name[30];
    float price;
    int quantity;
};

void update(struct stores *product, float p_inc, int q_inc) {
    product->price += p_inc;
    product->quantity += q_inc;
}

float total_value(struct stores *product) {
    return product->price * product->quantity;
}

int main() {
    struct stores item = {"Laptop", 45000.00, 10};
    
    printf("Original: %s, Price: %.2f, Quantity: %d\n", 
           item.name, item.price, item.quantity);
    printf("Total value: Rs. %.2f\n\n", total_value(&item));
    
    update(&item, 2000.00, 5);
    printf("After update: %s, Price: %.2f, Quantity: %d\n", 
           item.name, item.price, item.quantity);
    printf("Total value: Rs. %.2f\n", total_value(&item));
    
    return 0;
}
Output:
Original: Laptop, Price: 45000.00, Quantity: 10
Total value: Rs. 450000.00

After update: Laptop, Price: 47000.00, Quantity: 15
Total value: Rs. 705000.00