The ability to pass around references to objects is fundamental in many programming languages. I'm assuming the question is referring to the concept of "pointers" more broadly, encompassing not merely pointers that identify "root-level" objects or allocations that an implementation has created, but also pointers that may identify other kinds of regions of storage.
Pointer manipulations, often including the ability to convert pointers to integers, perform computations, and then convert the resulting integers back to pointers, provide a means by which human knowledge related to regions of memory and what may be done with them, can be applied to the process of instructing computers to do those things.
As a simple example, on systems that can only accommodate one program in memory at once (which used to be common in many microcomputer systems, and remains common in the embedded systems realm), any memory that isn't being used by the current program won't be used by anything at all until the current program exits. Thus, it would be useful to allow a program to, on startup, identify all space that isn't used for anything else and treat it as a blob the program can use as it sees fit, adapting itself to the amount of space that happens to be available.
On a typical embedded systems compiler, this may be accomplished by instructing the linker to automatically generate a symbol that points to just past the last object which it placed near the bottom of storage, and another that points to the first object that it placed near the top. Any storage between these two addresses then be used as the program sees fit. The C Standard doesn't allow for the possibility that a programmer might know anything about the relationship between two symbols that--from the compiler's point of view--would have nothing special about them, but when using implementations that are designed to be suitable for systems programming, a programmer can write memmory-allocation functions which manipulate pointers to access storage within this area.
When enabling some compilers' optimizations, however, there can be a problem. Suppose a program does something like:
extern unsigned char heap_start[], heap_end[];
void heap_init(void)
{
for (unsigned char *p=heap_start; p != heap_end; p++)
*p=0;
p[-1]=255;
}
int sneaky_test(int x)
{
heap_start[x]=0;
heap_init();
return heap_start[x];
}
If sneaky_test
happens to be passed a value one less than the displacement between heap_end
and heap_start
, then heap_init
will set heap_start[x]
to 255, and the sneaky_test
function should return 255. Some compilers, however, might recognize that because the pointer p
used in the statement p[-1]=255;
will always equal heap_end
, that expression may be replaced with heap_end[-1]=255;
, and since nothing that happens between heap_start[x]=0
and return heap_start[x]
sets any storage that could be derived from heap_start
to anything other than zero, the compiler may generate code that always returns zero.
Unfortunately, the way free compilers, or compilers based upon them, have evolved, it is often much easier to determine the sequence of machine operations that would need to be done to perform a task, and even to write source code that will on any given version of the compiler yield a machine code program that, if inspected, would be found to perform that sequence of operations, than to write source code that would, by specification, produce a usable machine code program. In most cases where a programmer might need to perform operations like the above, a compiler wouldn't happen to find "optimizing" transforms that would allow it to skip steps that are essential to the task at hand, even if it would have performed the transformations if it had found them, and thus code which would be usefully processed by today's compiler might fail with future compilers that "optimize" more aggressively.
sun.misc.Unsafe
. $\endgroup$