It seems like in language holy wars, people constantly denigrate any feature they don't find particularly useful as being "just syntactic sugar". The line between "real features" and "syntactic sugar" tends to get blurred in these debates. What do you believe is a reasonable and unambiguous definition of syntactic sugar that avoids it being defined as any feature the speaker/writer doesn't find useful?
How about this: "syntactic sugar is a convenience shorthand for some functionality that does not introduce any meaningful layer of abstraction."
a->b, which, as you point out, is equivalent to
(*a).b. Does this notation allow you to consider the code it's in any useful, otherwise hidden manner? No, so it's syntactic sugar.
a[i] == *(a + i). Think about any C program that uses arrays in any substantive way. Can you imagine trying to comprehend it without the
 notation? With multidimensional arrays? It is meaningful to consider arrays as whole units, not as a reference to the start of a contiguous block of memory. While it does help to know how arrays work in C if you're planning on doing complicated things with them, it is unproductive to always have to think "I need to store the two bits of memory 2*i bytes to the right of the memory location referenced by
a." The whole point of an array is the ability to abstract away the process of storing a sequence as a coherent unit. The
 notation facilitates this abstraction. It's not syntactic sugar.
This is not to imply that syntactic sugar is always bad thing. Like many alliterations, it has become an epithet and pitted against "real features." But LISP and Scheme, for example, would be unreadable if not for the
let shorthand (and others).
The ternary operator,
<pred> ? <cnsq> : <alt>, is another example. Syntactic sugar can help to organize programs and remove redundant code, which may save in maintenance down the line. Syntactic sugar may sometimes be preferable to piling on "real features" if it helps to remove syntactic barriers to programming.
To quote R^5RS, "Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary." IMHO, syntax can qualify as a weakness and restriction and so letting programmers get away from syntax can increase a language's expressivity.
IMHO I don't think you can have a definition for syntactic sugar, because the phrase is BS and is likely to be used by people that talk about "real programmers" using "real tools" on "real operating systems"
Its BS because the idea of "real features" or "syntactic sugar" is like the No true Scotsman fallacy. In that the phrases are "ad hoc attempts to retain an unreasoned assertion". The assertion here is that a feature here is trivial because you could use a "real feature" instead.
Its BS because some argue the use of sugar should be avoided because you can write a bug but you should stick to it because its harder to write bugs. Isn't that awesome. The same phrase leads to exactly opposite conclusions using the same logic.
Its BS because no one cites usability studies or defect count studies, to back up their readability or maintainability or likely defect arguments.
However if you want to say two things are sugar if they are semantically equivalent and that helps go ahead and define it anyway you want.
If you want to be dismissive of someone you can also use the phrase.
Here is a very rigorous definition for a related concept: expressiveness, by
On the Expressive Power of Programming Languages [Postscript was the only free form I could find.]
Also see this entry on the Java language and closures.
Effectively, something is syntactic sugar if it can be changed to a form without the syntax by only making local changes. If, for example, without the syntactic form, you would need to change several different code locations, or move fragments to other locations, then it is not sugar.
That said, syntactic sugar is fine when used appropriately. I think any Scheme programmer would rather there be a
let special form than to have to create a new anonymous function and then apply it, which would do the same thing. The purpose is to make code clearer.
I think the term syntactic sugar indicates an alternate syntax to express the same underlying semantics.
Take for example a programming language A that has an operation
sum that can add up a list of integers of arbitrary length. In this language we can write the expressions
sum  sum [3, 4, 5, 1] sum [2, 7]
whose results are 0, 13, and 9, respectively.
Now, suppose that we realize that 90% of the times we use
sum with two arguments, and therefore we introduce, for convenience, the new notation
2 + 7
which is just syntactic sugar for
sum [2, 7].
Now take a second language B that has no addition operation whatsoever. We may have operators like
=, allowing us to compare numbers, but no way to add numbers. In release 2 of language B, we introduce a new addition operation with syntax
2 + 7
which adds numbers as usual.
In the context of language A, the
+ notation is syntactic sugar (it is an alternate, simplified and ad-hoc notation that can be used instead of the
sum [...] notation). Similarly, as has been pointed out in Hoa Long Tam's answer, in C the notation
p->field is syntactic sugar for
In the context of language B, the
+ notation is not syntactic sugar (it is the only valid syntax used for the sum operation). Similarly, if C could only access struct members through pointers and if did not have the notation
(*p).field, then the notation
p->field would not be syntactic sugar.
In my opinion, there are some misunderstandings about syntactic sugar that can be traced back to a confusion regarding programming language semantics. The reasoning goes like this:
- The semantics of a program is what a program computes.
- The expressive power of a programming language is represented by the computations that can be described in that language.
- Two programming languages that can describe all computable functions (as defined using Turing machines) have the same expressive power...
- ... and therefore only differ in syntax.
- Corollary: any extension of a Turing-complete language is only syntax (syntactic sugar) because you do not change the expressive power of the language.
The above line of reasoning leads to generic assertions like "syntactic sugar cannot be defined properly", it is a "matter of taste", or "every programming language feature is, after all, just syntactic sugar".
I think the main problem in the above argument is that semantics is not only about what can be computed by a program, but also about how it is computed, i.e. what primitive constructs are used and how they are combined.
So for example, objects are not syntactic sugar for the underlying bit configurations and bit transformations, they are a construct that allows to model data and operations and to describe computations. Computing with objects, methods, method calls, is not the same as computing with bytes, processor registers, memory addresses (even if the two computations have the same result, and even if the second computation is used to implement the first).
I made this description a bit long but I think it is an important aspect that I haven't seen addressed in other answers.
Bottom line: syntactic sugar is an alternate (possibly more convenient) syntax for a construct that is already in a language and has already a well-defined syntax and semantics. The new syntax (syntactic sugar) differs from the existing one but has the same semantics. If you introduce a new construct in a language and new syntax for it, then you do not have syntactic sugar.
To answer my own question, a feature is syntactic sugar if and only if it it was included primarily to enhance aesthetics and readability and can be trivially translated in a roughly one-to-one fashion into the de-sugared version. (By roughly one-to-one I mean modulo trivial things like the order of commutative operations, variable names and whitespace.)
Any feature that could only be de-sugared with a significant amount of programmer discipline is not syntactic sugar. As a subset of this, any feature that increases type safety is not syntactic sugar, since manually enforcing type safety via programmer discipline is highly non-trivial. For example, C++'s object system is more than syntactic sugar on top of C pointer cast polymorphism.
Any feature that would require either significant code duplication or a major redesign if removed is not syntactic sugar, since neither of these is a trivial undertaking. For example, templates are not just syntactic sugar because getting the equivalent functionality without them would require tons of clone-and-modify.
Example of things that are syntactic sugar:
a[i] instead of
*(a + i)
a -> b instead of
foreach syntax instead of manually typing out iterator syntax.
All operator overloading is pure syntactic sugar.
Does this sound like a fair and reasonably unambigous definition?
"Syntactic sugar" is not a rigorously-defined term. Depending on who you ask, you will most likely get one of the following sorts of definitions:
- No true Scotsman approach. Things I like are real programming, and things I don't like are syntactic sugar.
- Everything after MIPS was syntactic sugar, and even some of those instructions probably aren't necessary.
- Anything that makes programming easier for somebody somewhere is useful and not syntactic sugar. Since a feature wouldn't have been added if nobody found it useful in any circumstance, it follows that every existing feature is not syntactic sugar. Hypothetical features may be syntactic sugar, until someone decides that feature is useful to them.
I'm not sure about within the fields of the computer sciences, but with the field of logic there are the concepts of conservativeness and eliminability of definitions  seem to be in the same vein.
Applying the Curry-Howard correspondence, one might be able to come up with a parallel concept regarding “syntactic sugar”.