OK, this is of no serious consequence, but it’s been bugging me for a
while: Is there a reason for the distinction between the -> and . operators?
Of course, the current rule is that . acts on a struct, and -> acts on
a pointer-to-struct (or union). But here’s how it works in practice.
Let s be a struct incuding an element x, and let ps be a pointer to a struct of the same form.
If you write
s->x
the compiler will spit out a warning in the way of
You meant s.x. Please retype that and recompile.
If you write
ps.x
the compiler will spit out a warning in the way of
You meant ps->x. Please retype that and recompile.
Because the compiler knows the type of both s and ps at compile time, it has all the information it needs to interpret what the correct operator would be. I suspect that this isn’t like other warnings (like a missing semicolon), in that there is no ambiguity about the correct fix.
So here’s a hypothetical proposal to the C1x standards committee (that would never be considered, because the ISO is on a conservative streak):
Given the expression lhs.rhs, if lhs is a struct or union type,
then the expression shall refer to the element of lhs named rhs.
If lhs is of type pointer-to-struct or -union, then this shall be
interpreted as (*lhs).rhs.
This would certainly save us all time, and make it easier for people to learn C [and I’ve taught enough C to say with authority that learners find the -> thing to be either confusing or annoying.]
There’s even precedent, where C does a handful of similar things. E.g., for implementation reasons, function declarations are always cast to pointer-to-function, so f(x,y) and (*f)(x,y) will both work regardless of whether f was declared as a function or a pointer to function.
So, my question: what’s wrong with this proposal? Can you think of examples where there would be fatal ambiguity between ps.x and s.x, or why keeping the mandatory distinction is otherwise useful?
Well, if you really wanted to introduce that kind of functionality into the specification of C language, then in order to make it “blend” with the rest of the language the logical thing to do would be to extend the concept of “decay to pointer” to struct types. You yourself made an example with a function and a function pointer. The reason it works that way is because function type in C decays to pointer type in all contexts, except for
sizeofand unary&operators. (The same thing happens to arrays, BTW.)So, in order to implement something similar to what you suggest, we could introduce the concept of “struct-to-pointer decay”, which would work in exactly the same way as all other “decays” in C (namely, array-to-pointer decay and function-to-pointer decay) work: when a struct object of type
Tis used in an expression, its type immediately decays to typeT*– pointer to the beginning of the struct object – except when it’s an operand ofsizeofor unary&. Once such a decay rule is introduced for structs, you could use->operator to access struct elements regardless of whether you have a pointer to struct or the struct itself on the left-hand side. Operator.would become completely unnecessary in this case (unless I’m missing something), you’d always use->and only->.The above, once again, what this feature would look like, in my opinion, if it was implemented in the spirit of C language.
But I’d say (agreeing with what Charles said) that the loss of visual distinction between the code that works with pointers to structs and the code that works with structs themselves is not exactly desirable.
P.S. An obvious negative consequence of such a decay rule for structs would be that besides the current army of newbies selflessly believing that “arrays are just constant pointers”, we’d have an army of newbies selflessly believing that “struct objects are just constant pointers”. And Chris Torek’s array FAQ would have to be about 1.5-2x larger to cover structs as well 🙂