Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.
Pointer Arithmetic in C
Learn which operations are legal on pointers, how the compiler scales each step by the element size, and why it matters in embedded C programming.
What You Will Learn
This lecture of our free C programming course introduces pointer arithmetic. In this free C tutorial you will see why p + 1 does not always mean “one byte further”, how int, float, double and char pointers step differently, and how to prove it with a short program. These ideas are the foundation of array traversal and buffer handling in embedded C programming.
- The three kinds of arithmetic that are allowed on pointers
- Scaling: how the base type sets the step size
- Increment, decrement, adding and subtracting integers
- Checking step sizes with a runnable program
- Why arithmetic belongs with arrays
Prerequisites
You should know how pointer types determine the size of the object read (previous lecture) and be comfortable with one-dimensional arrays from the earlier chapter of this free embedded C course.
Which Arithmetic Is Allowed on Pointers?
Ordinary numbers support every arithmetic operator, but pointers do not. Multiplying two addresses or dividing one by a number has no meaning, so C forbids it. The valid operations are limited to the following:
| Operation | Example | Result |
|---|---|---|
| Add an integer to a pointer | p + 3, p += 3 | A pointer 3 elements ahead |
| Subtract an integer from a pointer | p - 2, p -= 2 | A pointer 2 elements behind |
| Increment or decrement | p++, --p | Move one element forward or back |
| Subtract two pointers of the same type | q - p | An integer count of elements between them |
Comparing two pointers with ==, < and similar operators is also allowed when both point into the same array. Adding two pointers is not allowed.
Scaling: The Compiler Does the Multiplying
Pointer arithmetic counts in elements, not bytes. When you write p + n, the compiler computes the new address as the old address plus n * sizeof(*p). You never write that multiplication yourself, and that is the whole point: the same source line moves by the right amount for any type.
| Pointer type | sizeof(*p) | Effect of p + 1 | Effect of p + 3 |
|---|---|---|---|
char * | 1 | +1 byte | +3 bytes |
int * | 4 on most targets | +4 bytes | +12 bytes |
float * | 4 | +4 bytes | +12 bytes |
double * | 8 | +8 bytes | +24 bytes |
Many older books use a 2-byte int in their examples, so their addresses step by 2. On today’s compilers an int is normally 4 bytes, so we use that here. The idea is identical, only the numbers differ.
int pointer stepping through an array of four ints
Suggested image 3: a row of four array cells with a pointer marker moving by one element at a time. ALT text: “free C tutorial pointer arithmetic scaling by element size”.
Program: Prove the Step Sizes
This original program measures how far p + 1 moves for each type by casting both pointers to char *, which counts raw bytes. It then walks an int pointer forward and backward. Save it as ptr_arith.c.
#include <stdio.h>
int main(void)
{
char ca[4] = {'a','b','c','d'};
int ia[4] = {10, 20, 30, 40};
float fa[4] = {1.5f, 2.5f, 3.5f, 4.5f};
double da[4] = {0.1, 0.2, 0.3, 0.4};
char *pc = ca;
int *pi = ia;
float *pf = fa;
double *pd = da;
printf("char : p+1 is %td byte(s) ahead\n", (char *)(pc + 1) - (char *)pc);
printf("int : p+1 is %td byte(s) ahead\n", (char *)(pi + 1) - (char *)pi);
printf("float : p+1 is %td byte(s) ahead\n", (char *)(pf + 1) - (char *)pf);
printf("double : p+1 is %td byte(s) ahead\n", (char *)(pd + 1) - (char *)pd);
printf("\nint steps from start (byte offset, value):\n");
pi++; printf("pi++ -> %td, %d\n", (char *)pi - (char *)ia, *pi);
pi += 2; printf("pi += 2 -> %td, %d\n", (char *)pi - (char *)ia, *pi);
pi -= 3; printf("pi -= 3 -> %td, %d\n", (char *)pi - (char *)ia, *pi);
return 0;
}
$ gcc -Wall -Wextra -std=c11 -o ptr_arith ptr_arith.c
$ ./ptr_arith
char : p+1 is 1 byte(s) ahead
int : p+1 is 4 byte(s) ahead
float : p+1 is 4 byte(s) ahead
double : p+1 is 8 byte(s) ahead
int steps from start (byte offset, value):
pi++ -> 4, 20
pi += 2 -> 12, 40
pi -= 3 -> 0, 10
Follow the second block by hand: starting at offset 0, pi++ goes to offset 4 (value 20). Adding 2 jumps two elements, 8 bytes further, to offset 12 (value 40). Subtracting 3 returns to offset 0 (value 10). The array itself was never modified, only the pointer moved.
Why char Pointers Look Like Ordinary Arithmetic
For a char pointer, the element size is 1, so adding n really adds n bytes. That is why byte-oriented code such as UART buffers and string handling feels like plain integer maths. Switch to a wider type and the scaling quietly appears. This is also why you must never mix up the types: moving a uint32_t * by 1 jumps four bytes, which is wrong if you meant to advance one byte of a packet.
Hand Trace: Predict Before You Run
The best way to master pointer arithmetic is to calculate the result on paper first. Imagine an int array whose first element lives at address 3000, with a 4-byte int. Let p point to it. Work through these steps and compare with what the machine does.
| Statement | Calculation | p afterwards |
|---|---|---|
p++; | 3000 + 1 * 4 | 3004 |
p = p + 5; | 3004 + 5 * 4 | 3024 |
p--; | 3024 – 1 * 4 | 3020 |
p = p - 3; | 3020 – 3 * 4 | 3008 |
Now repeat the exercise for a double pointer starting at 5000, where each step is 8 bytes, and for a char pointer starting at 7000, where each step is 1 byte. You will notice that the written code is identical in all three cases. Only the declared type changes the arithmetic, and that is exactly what makes C source portable between a 16-bit, 32-bit and 64-bit target when you rely on the compiler instead of hard-coded sizes.
Portable code tip
Never hard-code the number 4 when you mean “the size of an int”. Write sizeof(int) or, better, use pointer arithmetic and let the compiler do the scaling for you.
Pointer Arithmetic in Embedded Systems
- Buffers: a DMA or UART receive buffer is walked with a pointer that advances after every byte or sample.
- Register blocks: a peripheral with consecutive 32-bit registers can be reached with
base + indexon avolatile uint32_t *. - Parsers: a protocol parser keeps a pointer and bumps it by the size of each field it consumes.
Common Mistakes
- Adding a byte count to a typed pointer.
pi + 4moves 16 bytes, not 4. To move by bytes, cast to unsigned char * first. - Using a pointer that has stepped out of its array. The result is undefined behaviour, even if you never dereference it more than one past the end.
- Doing arithmetic on void *. Standard C does not allow it because the element size is unknown, although some compilers accept it as an extension.
- Subtracting pointers into different arrays. The result is meaningless.
Best Practices
- Use pointer arithmetic on arrays and buffers, where the memory layout is guaranteed.
- Write the loop bound clearly, such as a pointer to one past the end.
- Prefer clear names like
rx_headandrx_endover single letters in firmware. - Print offsets with
%td, which is the format for pointer differences.
Key Takeaways
- Only add or subtract integers, increment, decrement and subtract two same-type pointers.
- Pointer arithmetic counts elements, and the compiler multiplies by sizeof(*p).
- int, float, double and char pointers step by different byte amounts.
- Moving a pointer does not change the variable it used to point to.
Conclusion
Scaling is the key idea behind pointer arithmetic. Once you see that p + 1 means “the next element”, array indexing, buffer walking and register access all become natural. In the next lecture of this free embedded C course we separate two look-alike expressions, *(p + 2) and p += 2, and learn how to keep pointers on valid memory.
Frequently Asked Questions
What arithmetic can I do on a pointer in C?
Add or subtract an integer, increment, decrement, and subtract two pointers of the same type.
Why does p + 1 move four bytes for an int pointer?
The compiler scales the integer by sizeof(int), which is usually 4.
Can I add two pointers?
No, the sum of two addresses has no meaning and the compiler rejects it.
How do I move a pointer by exactly one byte?
Cast it to unsigned char * and add 1.
Does incrementing a pointer change the variable?
No. Only the pointer’s stored address changes.
Is pointer arithmetic used in embedded programming?
Yes, constantly, for buffers, register arrays and protocol parsing.
Practise Pointer Arithmetic
Change the arrays in the demo to short and long long, and predict the step sizes before you run it. Then continue with our free C programming course.
Need Help or More Information?
Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments