A pointer is a variable whose value is an address.
Consider an ordinary integer variable:
The variable number stores the value 25 somewhere in memory. We can obtain its address using the address-of operator:
A pointer can then store that address:
The relationship can be viewed simply as:
number
|
| stores 25
↓
[ 25 ]
ptr
|
| stores address of number
↓
[ address of number ]
The pointer does not normally store the value 25 itself. It stores information about where number is located.
C pointers can point to objects, functions, or have a null pointer value indicating that they do not point to an object or function.

How Do Pointers Work in C?
Pointers work through two fundamental operations:
- Getting an address
- Dereferencing an address
1. Getting an Address
The & operator returns the address of an object.
c
int age = 28;
printf("%p", (void *)&age);
&age represents the address of age.
2. Dereferencing a Pointer
The * operator can be used to access the object pointed to by a pointer.
c
int age = 28;
int *ptr = &age;
printf("%d", *ptr);
Output:
The pointer ptr contains the address of age, while *ptr accesses the value stored in age. The C language defines pointer indirection as a way to access the object referred to by a pointer.
Declaration of a Pointer in C
The basic syntax for pointer declaration is:
For example:
c
int *ptr;
float *price;
char *letter;
The data type tells C what type of object the pointer is intended to point to. For example:
means ptr is a pointer to int.
You can also declare multiple pointers:
Both p1 and p2 are pointers to int.
A common beginner mistake is writing:
Here, only p1 is a pointer. p2 is an ordinary int variable. C’s declaration syntax treats the * as part of the declarator, so each pointer variable needs its own *.
Initialization of a Pointer in C
Declaring a pointer and giving it a valid address are two different things. For example:
declares the pointer but does not give it the address of a valid int object.
A safer initialization is:
c
int number = 50;
int *ptr = &number;
Now ptr points to number.
You can also initialize a pointer to the null pointer value:
A null pointer indicates that the pointer does not currently point to an object or function.
Why initialize a pointer?
An uninitialized automatic pointer may contain an indeterminate value. Dereferencing such a pointer can result in undefined behavior. Therefore, initialize a pointer with a valid address or an appropriate null pointer value before using it.
How to Use Pointers in C
A simple example shows the basic use clearly:
c
#include
int main(void)
{
int number = 25;
int *ptr = &number;
printf("Value: %d\n", number);
printf("Address: %p\n", (void *)&number);
printf("Pointer value: %p\n", (void *)ptr);
printf("Value through pointer: %d\n", *ptr);
return 0;
}
The important relationship is:
number → 25
ptr → address of number
*ptr → 25
You can also modify the original variable through the pointer:
c
int number = 25;
int *ptr = &number;
*ptr = 40;
printf("%d", number);
Output:
Changing *ptr changes number because ptr refers to number.
Pointer Operators in C
Two operators are particularly important when learning pointers.
| Operator | Purpose | Example |
|---|
& | Gets the address of an object | &number |
* | Dereferences a pointer | *ptr |
For example:
c
int number = 10;
int *ptr = &number;
Here:
&number → address of numberptr → stores that address*ptr → value at that address
The * symbol has different meanings depending on context. In a declaration such as int *ptr, it indicates a pointer type. In an expression such as *ptr, it performs indirection.
What Are the Types of Pointers in C?
Pointers can be classified according to what they point to or how they are used.
1. Integer Pointer
c
int number = 10;
int *ptr = &number;
ptr points to an integer.
2. Character Pointer
c
char letter = 'A';
char *ptr = &letter;
ptr points to a character.
3. Float Pointer
c
float value = 12.5f;
float *ptr = &value;
ptr points to a floating-point value.
4. Pointer to Pointer
A pointer can also store the address of another pointer.
c
int number = 10;
int *ptr = &number;
int **pptr = &ptr;
The relationship is:
pptr is therefore a pointer to a pointer to int.
5. Void Pointer
A void * can hold a pointer to an object of any object type and is commonly used in generic interfaces such as those involving dynamic memory and library functions.
Example:
c
int number = 10;
void *ptr = &number;
Before dereferencing a void *, it needs to be used with the appropriate object type.
6. Function Pointer
A function pointer stores the address of a function. For example:
c
int add(int a, int b)
{
return a + b;
}
int (*operation)(int, int) = add;
The function can then be called through the pointer:
c
int result = operation(10, 20);
Function pointers are useful for callbacks and selecting functions dynamically.
7. Null Pointer
A null pointer does not point to an object or function.
It is often used to represent “no valid object” or “no target.”

Pointer Arithmetic in C
Pointer arithmetic is particularly useful when working with arrays.
Suppose:
c
int numbers[] = {10, 20, 30, 40};
int *ptr = numbers;
Here, ptr points to the first element. You can move to the next element:
Now ptr points to the next int. You can also use:
c
printf("%d", *(ptr + 2));
This accesses the third element.
The important point is that pointer arithmetic is based on the pointed-to type. For a pointer to an array element, adding 1 moves to the next element rather than simply adding one byte. Pointer arithmetic is defined within the same array object, including the one-past-the-end position.
For example:
c
int numbers[4] = {10, 20, 30, 40};
int *ptr = numbers;
printf("%d\n", *ptr); // 10
printf("%d\n", *(ptr + 1)); // 20
printf("%d\n", *(ptr + 2)); // 30
This is one reason pointers and arrays are closely connected in C.
Pointers and Arrays
In many expressions, an array name is converted to a pointer to its first element.
For example:
c
int numbers[3] = {10, 20, 30};
int *ptr = numbers;
Here, ptr → numbers[0]. So:
accesses numbers[0]. And:
accesses numbers[1].
You can therefore process an array using a pointer:
c
#include
int main(void)
{
int numbers[] = {10, 20, 30, 40};
int *ptr = numbers;
for (int i = 0; i < 4; i++)
{
printf("%d\n", *(ptr + i));
}
return 0;
}
Pointers to arrays are a related but different concept from arrays of pointers. Parentheses matter when declaring more complex pointer types. For example:
is a pointer to an array of three integers.
Pointers and Functions
One of the most useful applications of pointers is passing an object’s address to a function.
Consider:
c
void update(int *value)
{
*value = 100;
}
It can be called as:
c
int number = 20;
update(&number);
After the function call:
The function receives the address of number, allowing it to modify the original object. This technique is commonly used when a function needs to modify caller-owned data or work with arrays and structures.
Pointers and Structures
Pointers are also widely used with structures.
Consider:
c
struct Student
{
int age;
float marks;
};
You can create a structure and a pointer to it:
c
struct Student student = {21, 85.5f};
struct Student *ptr = &student;
Members can be accessed through the pointer using the -> operator:
This is equivalent to:
c
printf("%d", (*ptr).age);
The -> operator makes structure pointers much easier to work with.
Size of a Pointer Variable in C
The size of a pointer is implementation-dependent. It should not be assumed that every pointer is 4 bytes or every pointer is 8 bytes.
You can determine the size on the current implementation using sizeof:
c
int *ptr;
printf("%zu", sizeof(ptr));
sizeof returns the size of an object or type in bytes as a value of type size_t.
For example, a particular system might use 8-byte object pointers, while another implementation may use a different size. Therefore, avoid writing code that assumes:
unless the target platform explicitly guarantees it.
Also remember that the size of a pointer is not the size of the object it points to. For example:
c
int number = 10;
int *ptr = &number;
sizeof(ptr) gives the size of the pointer, while sizeof(number) gives the size of the int object.
Applications of Pointers in C
Pointers are especially important in low-level and systems-oriented C programming.
Common applications include:
- Passing data to functions by address
- Processing arrays efficiently
- Working with strings
- Dynamic memory management
- Building linked lists, trees, and other data structures
- Accessing structure members
- Implementing callbacks with function pointers
- Working with buffers
- Interacting with hardware and memory-mapped resources in embedded systems
- Writing operating-system and device-level software
In embedded C, pointers are particularly important because firmware often needs to work with buffers, peripheral registers, memory addresses, and hardware interfaces. However, pointer use must follow the rules of the C implementation and the target hardware. An arbitrary address should never be treated as automatically safe to access.
Advantages and Limitations of Using Pointers in C
Pointers provide powerful capabilities, but they also require careful handling.
Advantages
- Allow functions to modify caller-owned objects.
- Make array and buffer processing flexible.
- Support dynamic data structures.
- Enable dynamic memory management.
- Allow function callbacks through function pointers.
- Provide the low-level memory access needed by many systems and embedded programs.
Limitations
Incorrect pointer handling can cause:
- Segmentation faults or other runtime failures
- Undefined behavior
- Memory corruption
- Use-after-free bugs
- Buffer overflows
- Dangling pointers
- Difficult-to-debug program errors
The power of pointers comes with responsibility: the pointer must refer to a valid object or otherwise be used according to the applicable C rules.
Common Mistakes When Working With Pointers in C
1. Dereferencing an Uninitialized Pointer
Avoid:
ptr has not been given a valid target.
Instead:
c
int number;
int *ptr = &number;
*ptr = 10;
2. Dereferencing a Null Pointer
Avoid:
c
int *ptr = NULL;
printf("%d", *ptr);
A null pointer does not point to a valid object.
3. Using a Pointer After Its Target Is No Longer Valid
For example, after dynamically allocated memory has been released, retaining and dereferencing the old pointer can create a dangling-pointer problem.
4. Accessing Outside an Array
For:
c
int numbers[3] = {10, 20, 30};
this is not valid:
c
printf("%d", numbers[3]);
The valid indexes are 0, 1, and 2. Pointer arithmetic has similarly strict boundaries. Going outside the permitted array range can result in undefined behavior.
5. Confusing *ptr with ptr
Consider:
c
int number = 25;
int *ptr = &number;
Here:
ptr → address*ptr → value
Mixing these two concepts is one of the most common beginner errors.
6. Assuming All Pointers Have the Same Size
Pointer size depends on the implementation and target architecture. Use sizeof when the actual size is needed.

Conclusion
A pointer is simply a variable that holds an address instead of a direct value – the rest of what makes pointers feel difficult is really about being careful with that address: initializing it, checking it before use, and releasing it correctly when it points to dynamically allocated memory. Once declaration, initialization, and dereferencing feel natural, concepts like function pointers, double pointers, and register-level access in embedded C stop being separate hurdles and start looking like the same idea applied in different contexts.