What are Illegal Pointer Operations in C- Embedded C Programming Course

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

Illegal Pointer Operations in C

See which pointer operations the compiler rejects, with real error messages and the correct midpoint formula. Free C programming course for embedded systems.

free c programming coursefree c tutorialc programming for embedded systemsembedded c programmingpointer arithmetic rules

What You Will Learn in This Lecture

  • Which arithmetic operators are legal on pointers and which the compiler rejects
  • Why arr / 2, q * 2 and q + q do not compile, and what the error messages say
  • How to compute a midpoint between two pointers the correct way
  • What pointer subtraction and comparison are allowed to do
  • How to cast a pointer to an integer type when you really need the address value in embedded C programming

Prerequisites

This lecture builds on pointer arithmetic and pointer loops from the earlier lectures of this free C programming course. You should already know that adding an integer to a pointer scales by the element size.

Why Illegal Pointer Operations Are Blocked

A pointer is an address with a type attached, not a plain number. Multiplying an address by two or dividing it by two produces a value that points to no object and has no meaning in the language. The compiler refuses these operations to stop you from building addresses that could never be valid. Valid pointer arithmetic is deliberately small: add or subtract an integer, subtract two pointers, compare two pointers.

OperationExampleLegal?Meaning
pointer + integerp + 3YesAddress of the element 3 positions ahead
pointer – integerp - 2YesAddress of the element 2 positions behind
pointer – pointerhi - loYes (same array)Number of elements between them
pointer comparelo < hiYes (same array)Which comes earlier in the array
pointer + pointerq + qNoAdding two addresses has no meaning
pointer * integerq * 2NoScaling an address has no meaning
pointer / integerarr / 2NoHalving an address has no meaning

Legal versus illegal

Legal p + np – np2 – p1p1 < p2p1 == p2
Rejected p + pp * np / np % np << n

What the Compiler Says

Try to compile this program. It is a deliberate error; do not expect it to run.

int main(void)
{
    int arr[8] = { 0 };
    int *q = arr / 2;
    int *p = q * 2;
    int *r = q + q;
    return 0;
}

GCC rejects all three lines:

error: invalid operands to binary / (have 'int *' and 'int')
    4 |     int *q = arr / 2;
error: invalid operands to binary * (have 'int *' and 'int')
    5 |     int *p = q * 2;
error: invalid operands to binary + (have 'int *' and 'int *')
    6 |     int *r = q + q;

Note the wording: “invalid operands to binary”. The compiler lists the operand types, which tells you what it saw. In the first case the array name arr has already decayed to int *, so the message shows int * rather than int[8].

Good to know: These are hard errors, not warnings, so they never slip into a build. The dangerous mistakes are the ones that **do** compile, such as a wrong cast or an out-of-range subscript.

The Right Way to Find a Midpoint

A common reason people reach for (lo + hi) / 2 is a binary search. With pointers you must not add them. Subtract first to get an element count, halve the count, and add it back to the lower pointer.

#include <stdio.h>

int main(void)
{
    int arr[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    int *lo = &arr[1];
    int *hi = &arr[6];
    printf("hi - lo = %td\n", hi - lo);
    printf("lo < hi : %d\n", lo < hi);
    printf("middle  : %d\n", *(lo + (hi - lo) / 2));
    return 0;
}
gcc -std=c17 -Wall -Wextra -o ptr_mid ptr_mid.c
./ptr_mid

Expected output:

hi - lo = 5
lo < hi : 1
middle  : 4

Computing the midpoint without adding pointers

arr 12 (lo)34567 (hi)8
step 1 hi – lo → 5 elements
step 2 5 / 2 → 2
step 3 lo + 2 → points at 4

The same formula, lo + (hi - lo) / 2, also avoids the integer overflow bug that affects (lo + hi) / 2 when lo and hi are plain integer indexes close to the maximum value. It is the form used in well-reviewed library code.

Pointer Comparison and Pointer Difference Rules

  • Relational comparisons (<, >, <=, >=) are only well defined for pointers into the same array object, or one past its end. Equality (==, !=) works between any compatible pointers.
  • The result of subtracting pointers has type ptrdiff_t, a signed integer type. Print it with %td.
  • Pointer difference counts **elements**. For the byte distance, cast both to char * first.
  • A pointer to void has no element size, so standard C does not allow arithmetic on it. Some compilers accept it as an extension; do not rely on it.

When You Really Need the Address as a Number

Embedded C programming sometimes needs the numeric value of an address, for example to check alignment or to program a DMA descriptor. Use the integer type designed for it, uintptr_t from <stdint.h>, rather than int or long.

#include <stdint.h>

uintptr_t addr = (uintptr_t)p;
int aligned = (addr % 4u) == 0;

Arithmetic is then done on the integer, not the pointer. The reverse conversion, (int *)addr, is allowed but only meaningful if the number really is a valid address for that type, which is exactly the situation with memory-mapped registers.

Warning: Do not store a pointer in an int on a 64-bit target. An int is only 32 bits, so the upper half of the address is lost and the pointer you convert back is wrong.

Worked Example: Reasoning About an Expression Before You Compile

When you meet an unfamiliar pointer expression, check three things in order. First, what is the type of each operand: array, pointer, integer? Second, is the operator allowed for that combination of types? Third, if it is allowed, what is the scale factor, meaning how many bytes does one step cover? Apply this to hi - lo in the program above. Both operands are int *, subtraction of two pointers into the same array is allowed, and the result is an element count, so the answer is 5 with no byte arithmetic involved. Apply it to q + q: two pointers, addition, not in the allowed list, so the compiler stops you. This habit catches most mistakes before you ever press the build button, and it is exactly the skill that output-prediction questions in a free C tutorial or a firmware interview are testing.

A second practical check concerns the difference between a pointer and the number it holds. You can print the address with %p after casting to void *, but you cannot reason about it with ordinary arithmetic. If you find yourself wanting to mask or round an address, for example to align a DMA buffer, that is the moment to convert to uintptr_t, do the integer work, and convert back at the last possible line.

Common Mistakes and Troubleshooting

MistakeSymptomFix
(lo + hi) / 2 on pointersCompile error: invalid operandsUse lo + (hi - lo) / 2
Printing hi - lo with %dWarning or wrong output on 64-bitUse %td
Comparing pointers into different arrays with <Unspecified resultCompare only within one array
Converting a pointer to intTruncated address on 64-bitUse uintptr_t
Arithmetic on void *Rejected or non-portableCast to char * or uint8_t * first

Best Practices, Performance and Security

  • Keep pointer arithmetic simple: add or subtract an integer, subtract two pointers, compare. Anything else deserves a comment or a redesign.
  • Do pointer-to-integer conversions in one small helper so the intent, and the portability risk, is in a single place.
  • On a 32-bit microcontroller, uintptr_t is 32 bits and the code works unchanged; on 64-bit hosts it is 64 bits. This is why it is the right type for portable test builds.
  • Security: integer checks on pointer values such as “is this address inside the buffer?” should compare against the buffer bounds, never against attacker-supplied lengths alone.
  • Treat compiler errors about pointer operands as helpful: they are the language telling you the expression has no meaning.

Summary and Key Takeaways

  • Legal: pointer plus or minus integer, pointer minus pointer, pointer comparison.
  • Illegal: pointer plus pointer, pointer times, divided by, or modulo an integer.
  • Midpoint is lo + (hi - lo) / 2.
  • Pointer difference counts elements and has type ptrdiff_t.
  • Use uintptr_t when you need an address as an integer.

Conclusion

Knowing what a pointer cannot do is as valuable as knowing what it can. The compiler errors in this lecture are your friends, because they stop you before the program runs. In the final lecture of this pointers chapter in our free embedded C course, we combine pointers to pointers with functions that modify the caller’s pointer, and finish with a quiz and interview questions.

Frequently Asked Questions

Why can I not add two pointers in C?

The sum of two addresses has no meaning. C only allows adding an integer to a pointer, which moves it by whole elements.

How do I find the middle of two pointers?

Use lo + (hi - lo) / 2. Subtract to get an element count, halve it and add the result to the lower pointer.

What type does pointer subtraction return?

ptrdiff_t, a signed integer type from <stddef.h>. Print it with the %td format specifier.

Can I multiply or divide a pointer?

No. The compiler rejects *, / and % on pointer operands. Convert to uintptr_t first if you genuinely need numeric arithmetic on an address.

Is comparing two pointers always allowed?

Equality comparison is. Relational comparison is only well defined when both pointers refer to the same array or one past its end.

Why use uintptr_t instead of unsigned long?

uintptr_t is defined to hold any object pointer, so it is the right width on every platform, while unsigned long can be too small, for example on 64-bit Windows.

Almost Done With Pointers

The last lecture in this chapter covers pointer to pointer, plus a quiz and interview questions.

Need Help or More Information?

Questions about this free C programming course, embedded C training or career guidance? Get in touch.

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *