If ptr is a pointer to some array of an element, then ptr++ increments ptr to point to the next element and ptr += i increments it to point i elements beyond the current element.
The integration of pointers, arrays and address arithmetic is one of the strengths of the language. Let us illustrate this by implementing a basic storage allocator. We’ll have two functions: memalloc and memfree. memalloc takes memory size n as argument and returns a pointer p to n consecutive character positions, which can be used by the caller for storing characters. memfree takes pointer p as argument and releases the storage, so that it can be re-used later.
In this implementation, the storage managed by memalloc and memfree is a stack (last-in, first-out). So, the last memory to be allocated should be the first one to be freed.
#define ALLOCSIZE 1000 static char buffer[ALLOCSIZE]; static char *buffptr = buffer; /* Initialise buffptr to point to the beginning of buffer. */ char *memalloc(int n) { if (buffptr - buffer + n >= ALLOCSIZE) /* Return NULL if it can't fit the remaining space. */ return NULL; char *p = buffptr; buffptr += n; return p; } void memfree(char *p) { if (p && p < buffer + ALLOCSIZE && p >= buffer) buffptr = p; }
In memalloc, we return NULL when we run out of space in buffer. NULL is a symbolic constant which is same as zero. Zero is the only integer value that may be assigned to a pointer and a pointer may be compared to a constant zero. C guarantees that zero is never a valid address for data, so a return value of NULL (or zero) can be used to signal an unexpected event, in this case, no space left.
In memfree, we check if p is not NULL and valid, and then we assign buffptr to p, freeing up the previous allocated space. We can see that relational operators like > may be used with pointers.
Also, a pointer and an integer can be added or subtracted as seen in the above code. In general, adding a number n to a pointer p of any type means the address of the n-th object beyond the one p currently points to.
Pointer arithmetic is consistent: if we are dealing with floats, which occupy more storage than chars, and if p were a pointer to float, p++ would advance to the next float. Thus, the above code we wrote for memalloc and memfree would work for floats by simply replacing char with float. All pointer manipulations automatically take the size of the objected pointed to, into account.
Valid pointer operations:
- Assignment of pointers of the same type.
- Adding or subtracting a pointer and an integer.
- Subtracting or comparing two pointers to members of the same array.
- Assigning or comparing to zero.
All other pointer arithmetic like adding two pointers, multiplying, dividing, shifting or adding a float/double, is illegal. Except void *, you cannot assign a pointer of one type to a pointer of another type without a cast.