For each object of such a composed type, there was already a way to mention the underlying object: index the array, call the function, use the indirection operator on the pointer. Analogical reasoning led to a declaration syntax for names mirroring that of the expression syntax in which the names typically appear. Thus,
int i, *pi, **ppi;
declare an integer, a pointer to an integer, a pointer to a pointer to an integer. The syntax of these declarations reflects the observation that i, *pi, and **ppi all yield an int type when used in an expression. Similarly,
int f(), *f(), (*f)();
declare a function returning an integer, a function returning a pointer to an integer, a pointer to a function returning an integer;
After almost 30 years, I think I just understood function pointer declaration syntax for the first time.
This has been a problem forever, the googleization of CS where everything is assumed to scale to gigabytes and therefore all that matters is big-O.
In systems that’s meaningless, what really matters is memory locality, loop placement, caching/lookaside and other features.
The JDk is an excellent example of both large scale and small scale optimization, the GC systems and much of the low-level features like locking use microoptimizations while the higher order data structure features use algorithmic optimizations.
Excellent post. I don’t have much experience with C but it was fascinating to read about the design decisions going into it and how even back then, backwards compatibility was very important for newly developed tools