Topic 12 — Pointers

A pointer is one of the most important concepts in C programming. Instead of storing an ordinary value such as 25, a pointer stores the address of an object.

Pointers connect many major C concepts: variables, arrays, strings, functions, structures, dynamic memory, files and low-level programming.

Why this topic matters: If you understand how values and addresses are connected, topics such as pass-by-value, arrays, dynamic memory allocation, strings and function pointers become much easier.

12.1 What Is a Pointer?

A pointer is an object whose value is the address of another object or, in some contexts, a function.

Consider:

int age = 20;

The variable age stores the value 20. The computer also associates age with a memory location. A pointer can store that memory address.

int age = 20; int *ptr = &age;

Here:

  • age stores 20.
  • &age produces the address of age.
  • ptr stores that address.
  • *ptr accesses the object stored at that address.
 Variable ┌─────────────┐ age │ 20 │ └─────────────┘ ▲ │ │ address of age │ ┌─────────────┐ ptr │ &age │ └─────────────┘ *ptr → 20 

12.2 Why Do We Need Pointers?

1. Access Memory Indirectly

A pointer allows a program to reach an object through its address.

2. Modify Caller Data

A pointer can be passed to a function so the function can modify an object belonging to the caller.

3. Work with Arrays

Arrays and pointers are closely related in C expressions and function calls.

4. Work with Strings

Character pointers are commonly used to process C strings.

5. Dynamic Memory

Functions such as malloc() and free() use pointers.

6. Advanced C

Structures, linked lists, trees, function pointers and system programming all depend heavily on pointer concepts.

12.3 The Two Most Important Pointer Operators

Operator Name Meaning Example
& Address-of Produces the address of an object &number
* Indirection / Dereference Accesses the object referred to by a pointer *ptr
Important: The same symbols can have different meanings depending on context. For example, * can mean multiplication in a * b, but dereference a pointer in *ptr. Similarly, & can mean address-of in &x, while it can mean bitwise AND in a & b.

12.4 Declaring a Pointer

The basic syntax is:

data_type *pointer_name;

Examples:

int *p; float *pricePtr; char *letterPtr; double *valuePtr;

The type tells the compiler what kind of object the pointer is intended to point to.

Remember: The declaration int *p; means that p has pointer type capable of pointing to an int object. It does not mean that p itself is an integer variable.

12.5 Understanding int *p

Beginners often read:

int *p;

as:

"p is an integer."

That is incorrect.

The correct interpretation is:

p is a pointer to an int.

 int number = 50; int *p = &number; number ┌──────────────┐ │ 50 │ └──────────────┘ ▲ │ │ │ points to │ p ┌──────────────┐ │ address │ └──────────────┘ 

12.6 Address-of Operator &

The address-of operator obtains the address of an object.

int number = 25; printf("%p\n", (void *)&number);

The actual address is implementation-dependent, so you should not expect the same address on every execution or every computer.

For printing a pointer value with printf, use %p and convert the pointer to void *:
printf("%p\n", (void *)ptr);

12.7 Dereference Operator *

The dereference operator accesses the object referred to by a valid pointer.

int number = 25; int *ptr = &number; printf("%d\n", *ptr);

Output:

25

The expression *ptr means:

"Access the object that ptr points to."

12.8 Reading and Modifying Through a Pointer

Dereferencing is not limited to reading. If the pointer points to a modifiable object, dereferencing can also be used to modify that object.

#include <stdio.h> int main(void) { int number = 10; int *ptr = &number; printf("Before = %d\n", number); *ptr = 50; printf("After = %d\n", number); return 0; }

Output:

Before = 10 After = 50

Why did number change? Because *ptr refers to the same object as number.

12.9 Pointer Program Tracing

Trace: number and ptr
int number = 10; int *ptr = &number; *ptr = 50;
Step Statement number ptr *ptr
1 int number = 10; 10 Not created yet
2 int *ptr; 10 Uninitialized Do not dereference
3 ptr = &number; 10 Address of number 10
4 *ptr = 50; 50 Same address 50
 points to ptr ─────────────────────► number ┌─────────┐ │ 50 │ └─────────┘ *ptr means: "Go to the object at ptr's address." 

12.10 Pointer and Pointee

The object whose address is stored in a pointer is often called the pointee.

int x = 100; int *p = &x;

Here:

  • p is the pointer.
  • x is the pointee.
  • &x is the address stored in p.
  • *p accesses x.

12.11 Pointer Initialization

A pointer should be initialized before it is dereferenced.

int value = 40; int *p = &value;

A pointer can also intentionally contain a null pointer value:

int *p = NULL;

A null pointer does not point to an object. It can be used to represent "no valid object is currently referenced."

Never dereference a null pointer. An expression such as *p is invalid when p == NULL.

12.12 NULL Pointer

A null pointer is a pointer value that compares unequal to a pointer to any object or function.

int *ptr = NULL; if (ptr == NULL) { printf("Pointer is NULL\n"); }

Modern C code may use nullptr in C23, but many educational and existing C programs use NULL. The important concept is that the pointer must not be dereferenced while it is null.

12.13 Uninitialized Pointers

This is dangerous:

int *p; *p = 10;

The pointer has not been given a valid target address. Dereferencing it can cause undefined behavior.

Use:

int value = 10; int *p = &value;

or, when there is no target yet:

int *p = NULL;

12.14 Pointer Types

Pointer Can point to
int * int object
char * char object
float * float object
double * double object
struct Student * struct Student object

The pointer type is important because pointer arithmetic and dereferencing depend on the pointed-to type.

12.15 Why Pointer Type Matters

Suppose:

int *p;

When *p is evaluated, the compiler interprets the object using the pointed-to type int.

Pointer arithmetic also uses the size of the pointed-to type. This becomes especially important when pointers are used with arrays.

A common misconception is: "All pointers are exactly the same because they store addresses." The representation may have similarities on a particular system, but different pointer types have different semantics, and not every pointer type is interchangeable without the appropriate conversion.

12.16 Pointer Size vs Pointed-to Object Size

Do not assume that the size of a pointer is the size of the object it points to.

int x = 10; int *p = &x; printf("%zu\n", sizeof(x)); printf("%zu\n", sizeof(p));

The exact sizes are implementation-dependent. A pointer's size does not have to equal the size of the pointed-to type.

12.17 Pointers and Functions

C passes function arguments by value. However, if a function receives a pointer value, the function can use that pointer to access the caller's object.

Example:

#include <stdio.h> void changeValue(int *p) { *p = 100; } int main(void) { int number = 20; changeValue(&number); printf("%d\n", number); return 0; }

Output:

100

The pointer itself is passed by value, but the copied pointer points to the same number object.

12.18 Program Tracing — Function + Pointer

 main() │ │ number = 20 │ │ changeValue(&number) ▼ changeValue(int *p) │ │ p receives a copy of number's address │ │ *p = 100 ▼ number becomes 100 │ ▼ return to main() 
Stage number p
Before function call 20
Inside function 20 Address of number
After *p = 100 100 Address of number
After return 100 Function's local pointer no longer exists

12.19 Swapping Two Numbers Using Pointers

A classic pointer application is swapping two objects inside a function.

#include <stdio.h> void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } int main(void) { int x = 10; int y = 20; printf("Before: %d %d\n", x, y); swap(&x, &y); printf("After: %d %d\n", x, y); return 0; }

Output:

Before: 10 20 After: 20 10
The function does not receive x and y directly. It receives their addresses, allowing it to access and modify the original objects.

12.20 Pointer Arithmetic

Pointers to elements of the same array support arithmetic operations within the array object and one-past-the-end position.

For example:

int numbers[] = {10, 20, 30, 40}; int *p = numbers; printf("%d\n", *p); printf("%d\n", *(p + 1)); printf("%d\n", *(p + 2));

Output:

10 20 30

The expression p + 1 does not mean "add one byte." It advances to the next int element.

12.21 Pointer Arithmetic and Array Elements

 int numbers[] = {10, 20, 30, 40}; Index: 0 1 2 3 │ │ │ │ ▼ ▼ ▼ ▼ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ 10 │ │ 20 │ │ 30 │ │ 40 │ └─────┘ └─────┘ └─────┘ └─────┘ ▲ │ p p + 1 → points to element 1 p + 2 → points to element 2 

The exact byte distance depends on sizeof(int), but C pointer arithmetic automatically scales by the size of the pointed-to type.

12.22 Incrementing a Pointer

int numbers[] = {10, 20, 30}; int *p = numbers; printf("%d\n", *p); p++; printf("%d\n", *p);

Output:

10 20

After p++, the pointer refers to the next array element.

12.23 Pointer Subtraction

Two pointers to elements of the same array can be subtracted. The result represents the number of elements between them.

int numbers[] = {10, 20, 30, 40, 50}; int *p1 = &numbers[1]; int *p2 = &numbers[4]; printf("%td\n", p2 - p1);

Output:

3

The result type is ptrdiff_t, for which %td is the appropriate printf conversion.

Pointer subtraction is meaningful only when the pointers refer into the same array object, including its one-past-the-end position where appropriate.

12.24 One-Past-the-End Pointer

C permits forming a pointer to one position past the last element of an array. It may be used for comparison and iteration, but it must not be dereferenced.

int numbers[] = {10, 20, 30}; int *p = numbers; int *end = numbers + 3; while (p != end) { printf("%d\n", *p); p++; }

Here end is valid as a pointer value for the iteration boundary, but *end must not be evaluated.

12.25 Pointers and Arrays

An array expression often converts to a pointer to its first element when used in most expressions.

int numbers[] = {10, 20, 30}; int *p = numbers;

Here p points to numbers[0].

Therefore:

numbers[0] *(numbers + 0) numbers[1] *(numbers + 1) numbers[2] *(numbers + 2)

refer to corresponding elements.

Arrays are not pointers. They are different types. An array expression commonly converts to a pointer to its first element, but the array object itself is not a pointer.

12.26 Array Indexing Is Related to Pointer Arithmetic

For an array or pointer expression, the notation:

a[i]

is defined in terms of pointer arithmetic as:

*(a + i)

For example:

int a[] = {5, 10, 15}; printf("%d\n", a[1]); printf("%d\n", *(a + 1));

Both print:

10

12.27 Pointers as Function Parameters for Arrays

When an array is passed to a function, the parameter declaration is adjusted so that the function receives a pointer to the first element.

#include <stdio.h> void printArray(int a[], int n) { for (int i = 0; i < n; i++) { printf("%d ", a[i]); } } int main(void) { int numbers[] = {10, 20, 30, 40}; printArray(numbers, 4); return 0; }

Output:

10 20 30 40
The function does not automatically receive the complete array object as a copied value. It receives a pointer to its first element, so the function also needs the array length separately.

12.28 Important Interview Point: sizeof and Array Parameters

Consider:

void showSize(int a[]) { printf("%zu\n", sizeof(a)); }

Inside this parameter declaration, a is adjusted to a pointer parameter. Therefore sizeof(a) gives the size of the pointer, not the size of the original array.

This is why a function commonly receives the array length separately:

void process(int a[], int n)

12.29 Pointers and Strings

A C string is an array of characters terminated by a null character '\0'. A pointer can point to its first character.

char name[] = "Bhavya"; char *p = name; printf("%s\n", p);

Output:

Bhavya

The pointer can also be used to traverse the characters:

while (*p != '\0') { putchar(*p); p++; }

12.30 Character Pointer vs Character Array

Declaration Meaning
char text[] = "Hello"; Creates a modifiable character array initialized from the string literal.
char *p = text; Pointer points to the first character of the array.

Be careful with:

char *p = "Hello";

The string literal should not be modified through p. Attempting to modify it results in undefined behavior.

12.31 Pointer to Pointer

A pointer can itself have an address. Therefore, another pointer can point to it.

int value = 25; int *p = &value; int **pp = &p;
 pp │ │ points to ▼ ┌───────────────┐ │ p │ │ address of │ │ value │ └───────────────┘ │ │ points to ▼ ┌───────────────┐ │ value │ │ 25 │ └───────────────┘ *pp → p **pp → value → 25 

12.32 Dereferencing Multiple Levels

int value = 25; int *p = &value; int **pp = &p; printf("%d\n", **pp);

Output:

25

Explanation:

  1. pp points to p.
  2. *pp gives p.
  3. **pp accesses the object pointed to by p.

12.33 Const and Pointers

The const qualifier can be combined with pointer declarations in several different ways.

Pointer to const data

const int *p;

The object should not be modified through p. The pointer itself may be changed to point elsewhere.

Const pointer

int *const p = &value;

The pointer itself cannot be changed to point elsewhere after initialization, but the pointed-to int can be modified through p.

Const pointer to const data

const int *const p = &value;

Neither the pointer itself nor the object can be modified through that pointer.

12.34 Pointer Compatibility

Pointer types should be used carefully. For example:

int *p; double *q;

These pointers are intended to point to different types of objects. Do not simply treat one as the other without an appropriate conversion.

The special pointer type void * can hold the address of an object of any object type and is widely used in generic C interfaces.

12.35 Void Pointers

int number = 50; void *ptr = &number;

A void * is a generic object pointer. Before dereferencing it, convert it to an appropriate pointer type or assign it to one.

printf("%d\n", *(int *)ptr);

The compiler cannot directly determine the pointed-to object type from void * alone.

Do not use void * as a replacement for understanding pointer types. It is useful for generic programming, but the programmer must still know what object the address actually refers to.

12.36 Pointer Comparisons

Pointers can be compared in specific meaningful situations. For example, pointers into the same array can be compared using relational operators.

int a[] = {10, 20, 30}; int *p = &a[0]; int *q = &a[2]; if (p < q) { printf("p comes before q\n"); }

Equality and inequality comparisons are also commonly used when checking for null pointers:

if (p == NULL) { printf("No object\n"); }

12.37 Pointer Safety

Many serious C bugs involve invalid pointers.

Null Pointer

Do not dereference a null pointer.

Dangling Pointer

Do not use a pointer after the object it referred to has ceased to exist.

Uninitialized Pointer

Initialize pointers before using them.

Out-of-Bounds Pointer

Do not access outside the valid array/object boundaries.

Wrong Pointer Type

Use compatible pointer types and conversions.

String Literal Modification

Never modify a string literal through a pointer.

12.38 Dangling Pointers

A dangling pointer is a pointer whose stored address is no longer a valid way to access the object it previously referred to.

For example, returning the address of an ordinary local variable from a function is invalid:

int *wrong(void) { int x = 10; return &x; }

When the function returns, the lifetime of the local object x has ended. The returned pointer cannot safely be dereferenced.

The pointer value may still contain an address-like bit pattern, but that does not make the pointed-to object valid. Pointer validity depends on the lifetime and storage of the object.

12.39 Common Pointer Mistakes

Mistake Problem
Dereferencing an uninitialized pointer Undefined behavior
Dereferencing NULL Invalid access
Accessing outside an array Undefined behavior
Using a pointer after object lifetime ends Dangling pointer / invalid access
Returning address of local automatic variable Pointer becomes invalid after function returns
Writing through a pointer to read-only data Invalid modification

12.40 Pointer vs Value

Expression Meaning
x The value stored in object x
&x Address of object x
p Address stored in pointer p
*p Object accessed through pointer p

12.41 Pointer Visualization

 int marks = 85; int *p = &marks; stores 85 ┌─────────────┐ marks ───────►│ 85 │ └─────────────┘ ▲ │ │ address │ ┌─────────────┐ p ───────────►│ &marks │ └─────────────┘ p = address of marks *p = 85 &marks = same address stored in p 
The pointer does not contain the value 85. It contains the address used to reach the object containing 85.

12.42 Complete Program Analysis

#include <stdio.h> void update(int *p) { *p = *p + 10; } int main(void) { int marks = 70; int *ptr = &marks; printf("Before = %d\n", marks); update(ptr); printf("After = %d\n", marks); return 0; }

Step 1 — Create the variable

int marks = 70;

An integer object named marks is initialized with 70.

Step 2 — Store its address

int *ptr = &marks;

ptr stores the address of marks.

Step 3 — Call the function

update(ptr);

A copy of the pointer value is passed to update.

Step 4 — Dereference

*p = *p + 10;

The function reads the caller's marks object through the pointer and changes it from 70 to 80.

Step 5 — Return to main

The updated value remains in marks.

Output:

Before = 70 After = 80

12.43 Pointers and Memory Layout — Conceptual View

The following is a conceptual model rather than a promise about exact addresses or physical memory organization.

 Object Pointer ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ value │◄─────────│ address │ │ │ │ │ └──────────────┘ └──────────────┘ ▲ │ │ │ └────────── *p ───────────┘ 

This mental model is enough to understand the basic relationship: a pointer provides an indirect route to an object.

12.44 Pointers and sizeof

Remember that sizeof measures the size of its operand's type or object representation, not the size of the memory region conceptually "owned" by a pointer.

int x = 10; int *p = &x; printf("%zu\n", sizeof(x)); printf("%zu\n", sizeof(p)); printf("%zu\n", sizeof(*p));

The third expression, sizeof(*p), corresponds to the size of the pointed-to type, int, without requiring the pointed-to object's value to be read.

12.45 Pointer Expressions to Remember

Expression Meaning
p Pointer value
*p Object pointed to by p
&x Address of x
p + 1 Next element for an appropriate array pointer
p - 1 Previous element for an appropriate array pointer
p == NULL Check whether pointer is null
**pp Dereference a pointer-to-pointer twice

12.46 Common Confusions

Confusion 1 — Pointer vs Address

&x is an expression that produces an address. A pointer object such as p stores a pointer value.

Confusion 2 — *p vs p

p is the pointer value. *p accesses the object referred to by that pointer.

Confusion 3 — Array vs Pointer

An array is not a pointer. An array expression commonly converts to a pointer to its first element in expressions.

Confusion 4 — C Pass-by-Reference

C has pass-by-value. Passing a pointer allows the function to access the same caller object, which provides reference-like behavior without changing C's parameter passing rule.

13.18 Quick Revision

📌 Pointer → stores an address.

📌 &x → address of x.

📌 *p → value at address stored in p.

📌 Pointer arithmetic is commonly used with arrays.

📌 Arrays and pointers are closely related, but they are not identical concepts.

📌 Pointers allow functions to modify caller data when addresses are passed.

📌 malloc() → dynamic allocation.

📌 calloc() → zero-initialized allocation.

📌 realloc() → resize allocation.

📌 free() → release allocation.

📌 NULL pointer → intentionally points to no valid object.
INTERACTIVE LEARNING

🎬 Pointers — Address and Dereference Flow

Follow the relationship between a variable, its address, a pointer, dereferencing, and modifying the original value.

PROGRAM TRACING

🔎 Program Tracing — Pointers

Trace how a pointer stores an address, modifies the original variable, and reads the updated value through dereferencing.

13.19 Quick MCQs

Select an answer first, then click Check Answer. A correct choice becomes green. If the answer is wrong, your choice becomes red and the correct option becomes green. The explanation appears below.

1. What does a pointer store?
2. Which operator obtains the address of a variable?
3. Which operator dereferences a pointer?
4. What does NULL represent for a pointer?
5. Which function allocates dynamic memory?
6. Which function releases dynamically allocated memory?
PRACTICE

13.20 🎯 Practice Problems

Pointers become clear through repeated address, dereference, array, string, and dynamic-memory practice. Use 💻 Solve It Yourself first, open Hint only when needed, and use Show Program after attempting the problem.

📈 Pointers Practice Progress
Solved 0 / 20
Completed with Solution 0
Total Score 0 / 2000
Completion 0%
A problem counts as Solved when all test cases pass without opening the full solution. Problems completed after viewing the official solution are tracked separately.
1. Value and Address Relationship

Problem 1: Read an integer, print its value, and verify that a pointer stores the address of that variable.

Input: One integer.

Output: Print the value and "Address Match = Yes".

2. Access a Variable Using a Pointer

Problem 2: Read an integer and print its value by dereferencing a pointer.

Input: One integer.

Output: Print the value obtained through *p.

3. Modify a Variable Using a Pointer

Problem 3: Read an integer and a new value. Modify the original variable through a pointer.

Input: Two integers: original value and new value.

Output: Print the modified variable.

4. Sum of Two Numbers Using Pointers

Problem 4: Read two integers and calculate their sum by dereferencing pointers.

Input: Two integers.

Output: Print the sum.

5. Swap Two Numbers Using Pointers

Problem 5: Swap two integers using a function that receives pointers.

Input: Two integers.

Output: Print the swapped values.

6. Maximum of Two Numbers Using Pointers

Problem 6: Find the larger of two integers using pointers.

Input: Two integers.

Output: Print the maximum value.

7. Print Array Elements Using a Pointer

Problem 7: Read N integers and print every array element using pointer notation.

Input: N followed by N integers.

Output: Print the elements separated by spaces.

8. Sum an Array Using Pointer Arithmetic

Problem 8: Find the sum of an integer array using pointer arithmetic.

Input: N followed by N integers.

Output: Print the sum.

9. Maximum Array Element Using Pointers

Problem 9: Find the maximum element of an integer array using pointer access.

Input: N greater than 0 followed by N integers.

Output: Print the maximum element.

10. Reverse an Array Using Pointers

Problem 10: Reverse an array in place using pointer-based swapping.

Input: N followed by N integers.

Output: Print the reversed array.

11. String Length Using a Pointer

Problem 11: Find the length of a string using pointer traversal.

Input: One line of text.

Output: Print the string length excluding the newline.

12. Reverse a String Using Pointers

Problem 12: Reverse a string in place using pointers.

Input: One line of text.

Output: Print the reversed string.

13. Count Vowels Using a Pointer

Problem 13: Count vowels in a string by traversing it with a pointer.

Input: One line of text.

Output: Print the vowel count.

14. Palindrome String Using Pointers

Problem 14: Check whether a string is a palindrome using left and right pointers.

Input: One line of text.

Output: Print "Palindrome" or "Not Palindrome".

15. Pointer to Pointer

Problem 15: Create a pointer to a pointer and print a variable's value through double dereferencing.

Input: One integer.

Output: Print the value using **pp.

16. Dynamic Integer Array with malloc()

Problem 16: Dynamically allocate an integer array using malloc(), read values, and print their sum.

Input: N followed by N integers.

Output: Print the sum, or Allocation Failed if memory allocation fails.

17. Initial Values with calloc()

Problem 17: Allocate an integer array using calloc() and print its initial values before assigning anything.

Input: One positive integer N.

Output: Print N zero values separated by spaces, or Allocation Failed.

18. Resize an Array with realloc()

Problem 18: Allocate N integers, resize the array to M integers using realloc(), read the new elements, and print the complete resized array.

Input: N, N integers, M where M >= N, followed by M-N new integers.

Output: Print all M elements, or Allocation Failed.

19. Structure Pointer

Problem 19: Create a structure variable and access its members through a structure pointer.

Input: Student roll number and marks.

Output: Print both values using the -> operator.

20. Function Pointer for Addition

Problem 20: Create a function pointer for an addition function and use it to calculate the sum of two integers.

Input: Two integers.

Output: Print their sum.

13.21 Key Takeaway

🎯 Remember this three-part rule:

&x → Address of x

p → Address stored in p

*p → Value at that address

Once this becomes clear, pointers become much easier to understand.
INTERVIEW PREPARATION

🎤 Pointers — Interview Questions

1. What is the difference between p, &x, and *p?
2. What is a NULL pointer?
3. What is a wild pointer?
4. What is a dangling pointer?
5. How are arrays and pointers related in C?
6. What does pointer arithmetic depend on?
7. Why should realloc() commonly use a temporary pointer?
8. What is a function pointer?
PLACEMENT TIPS

💡 Pointers — Placement Tips

  • Always separate the three ideas: address, pointer value, and dereferenced object.
  • Initialize pointers before dereferencing them, and check dynamically allocated pointers against NULL.
  • For array-pointer questions, remember arr[i] and *(arr + i) refer to the same element.
  • Pointer arithmetic is scaled by the pointed-to type; p + 1 moves to the next element of that type.
  • After free(p), do not dereference p. Setting it to NULL can help avoid accidental reuse.
  • For realloc(), use a temporary pointer so the original allocation is not lost if resizing fails.
EXTRA PRACTICE

✍️ Pointers — Extra Practice Questions

  1. Use pointers to find the second largest element of an array.
  2. Use pointer arithmetic to count positive and negative array elements.
  3. Copy one string to another using only pointers.
  4. Concatenate two strings using pointer traversal without strcat().
  5. Dynamically allocate a matrix using pointers and calculate each row sum.
  6. Create an array of function pointers for addition, subtraction, multiplication, and division.
← Previous Topic: Functions Next Topic: Structures & Unions →