56

Disclaimer: I know perfectly well the semantics of prefix and postfix increment. So please don't explain to me how they work.

Reading questions on stack overflow, I cannot help but notice that programmers get confused by the postfix increment operator over and over and over again. From this the following question arises: is there any use case where postfix increment provides a real benefit in terms of code quality?

Let me clarify my question with an example. Here is a super-terse implementation of strcpy:

while (*dst++ = *src++);

But that's not exactly the most self-documenting code in my book (and it produces two annoying warnings on sane compilers). So what's wrong with the following alternative?

while (*dst = *src)
{
    ++src;
    ++dst;
}

We can then get rid of the confusing assignment in the condition and get completely warning-free code:

while (*src != '\0')
{
    *dst = *src;
    ++src;
    ++dst;
}
*dst = '\0';

(Yes I know, src and dst will have different ending values in these alternative solutions, but since strcpy immediately returns after the loop, it does not matter in this case.)

It seems the purpose of postfix increment is to make code as terse as possible. I simply fail to see how this is something we should strive for. If this was originally about performance, is it still relevant today?

fredoverflow
  • 6,854
  • 8
  • 39
  • 46
  • 8
    The postfix variant is the most commonly used one, so if you're going to make a case against it, it better be a pretty strong case. And your first example is a bit of a straw man, since I suspect few people would actually code the `strcpy` method this way (for reasons you have already mentioned). – Robert Harvey Feb 01 '11 at 00:01
  • 4
    Why have either at all? – James McNellis Feb 01 '11 at 00:01
  • 31
    If we removed everything from the language that had the potential to confuse programmers, we wouldn't have very many features. The fact something isn't useful to you, or only very *rarely* useful, doesn't mean it should be snipped. If it's not relevant anymore, don't use it; the end. – Cody Gray - on strike Feb 01 '11 at 00:01
  • I suppose this has something to do with ancient "stupid" (non-optimizing) compilers, but I'm just guessing. –  Feb 01 '11 at 00:02
  • This is not a duplicate, but I [asked something similar](http://stackoverflow.com/questions/3948488/is-kr-teaching-bad-readability). Might be worth reading too. – alex Feb 01 '11 at 00:02
  • @Robert: I guess this boils down to personal preference, but I *never* use postfix increment, I *always* use prefix. For the sake of argument, we could define `i++` to have "prefix semantics" and provide no corresponding postfix alternative. Have we lost expressiveness? – fredoverflow Feb 01 '11 at 00:05
  • 4
    Yeah but then you can never do `int c = 0; c++;` – Biff MaGriff Feb 01 '11 at 00:06
  • 5
    @Cody: Because some things are both confusing and useful. He isn't confused by post-increment itself, he's confused about it's *usefulness*. We shouldn't have things that are useless, confusing or not. – GManNickG Feb 01 '11 at 00:34
  • @GMan: I see useless things all the time, all over the place. Does that mean I should take them out? It's not clear to me whether this is a purely academic question of "why is it there in the first place?", or "why is it *still* there, even though it's no longer useful?" I'm leaning towards the latter, in which case it seems completely absurd to break perfectly valid, existing code just because it uses syntax that, however minimally useful, isn't hurting anyone to leave there. – Cody Gray - on strike Feb 01 '11 at 00:45
  • 1
    @Cody: It's the former. (That said I'm in the rare group that's totally fine with breaking code with huge changes, if it means better code, but that's a separate issue.) – GManNickG Feb 01 '11 at 00:51
  • 2
    @Cody Gray: removing an operator isn't an option for the reasons that we all know. I think that the question was more about an historical prospective about the postfix increment operator; since it performs a quite a specific operation (*increment a var of 1 but return the value before the increment*) and it can be often confusing, it raises the question of why it was thought/included in first place in the language (common patterns/easier optimization/...). –  Feb 01 '11 at 00:51
  • 1
    A better example of a useless operator is unary `+`, in my book. –  Feb 01 '11 at 01:31
  • 1
    @caf: Unary `+` can be useful in macros that take `+` or `-` as an argument and use the argument operator in both unary and binary contexts. :-) –  Feb 01 '11 at 01:59
  • Just to be strict about this, unary plus also applies the integer promotions, see http://stackoverflow.com/questions/4280188/difference-between-and-in-c/4280624#4280624 for a (contrived) example of it actually doing something. – DigitalRoss Feb 01 '11 at 07:32
  • 2
    This should be migrated or closed. It's more a CS theory or programming question, and should no longer reside on SO as it's a very subjective (and due to the assertions the OP makes about readability, somewhat argumentative) question. There is no objectively correct answer that everyone can agree on. – Adam Davis Feb 02 '11 at 19:13
  • 3
    For all I know, the real answer involves a few people sitting around the lunch table and one of them saying saying "Hey, that increment operator you invented is really nifty. Could you do one that would increment the variable first and then evaluate it?" :-) – Blrfl Sep 23 '16 at 21:12

15 Answers15

33

It is, err, was, a hardware thing


Interesting that you would notice. Postfix increment is probably there for a number of perfectly good reasons, but like many things in C, it's popularity can be traced to the origin of the language.

Although C was developed on a variety of early and underpowered machines, C and Unix first hit the relative big-time with the memory-managed models of the PDP-11. These were relatively important computers in their day and Unix was by far better -- exponentially better -- than the other 7 crummy operating systems available for the -11.

And, it so happens, that on the PDP-11,

*--p

and

*p++

...were implemented in hardware as addressing modes. (Also *p, but no other combination.) On those early machines, all less than 0.001 GHz, saving an instruction or two in a loop must almost have been a wait-a-second or wait-a-minute or go-out-for-lunch difference. This doesn't precisely speak to postincrement specifically, but a loop with pointer postincrement could have been a lot better than indexing back then.

As a consequence, the design patterns because C idioms which became C mental macros.

It's something like declaring variables right after a { ... not since C89 was current has this been a requirement, but it's now a code pattern.

Update: Obviously, the main reason *p++ is in the language is because it is exactly what one so often wants to do. The popularity of the code pattern was reinforced by popular hardware which came along and matched the already-existing pattern of a language designed slightly before the arrival of the PDP-11.

These days it makes no difference which pattern you use, or if you use indexing, and we usually program at a higher level anyway, but it must have mattered a lot on those 0.001GHz machines, and using anything other than *--x or *x++ would have meant you didn't "get" the PDP-11, and you would might have people coming up to you and saying "did you know that..." :-) :-)

DigitalRoss
  • 531
  • 3
  • 7
  • 28
    [Wikipedia](http://en.wikipedia.org/wiki/PDP-11_architecture) says: "A (false) folk myth is that the instruction set architecture of the PDP-11 influenced the idiomatic use of the C programming language. The PDP-11's increment and decrement addressing modes correspond to the `−−i` and `i++` constructs in C. If `i` and `j` were both register variables, an expression such as `*(−−i) = *(j++)` could be compiled to a single machine instruction. [...] Dennis Ritchie unambiguously contradicts this folk myth. [[The Development of the C Language](http://cm.bell-labs.com/cm/cs/who/dmr/chist.html)]" – fredoverflow Feb 01 '11 at 00:20
  • 3
    Huh (From the link provided in FredOverflow's comment): "People often guess that they were created to use the auto-increment and auto-decrement address modes provided by the DEC PDP-11 on which C and Unix first became popular. This is historically impossible, since there was no PDP-11 when B was developed. The PDP-7, however, did have a few `auto-increment' memory cells, with the property that an indirect memory reference through them incremented the cell. This feature probably suggested such operators to Thompson; the generalization to make them both prefix and postfix was his own." –  Feb 01 '11 at 00:28
  • 3
    DMR only said that the PDP-11 was not the reason it was added to C. I said that too. What I said was that it was used to advantage on the PDP-11 and became a popular design pattern for that exact reason. If Wikipedia contradicts *that* then they are simply wrong, but I don't read it that way. It is phrased poorly on that W page. – DigitalRoss Feb 01 '11 at 00:30
  • @FredOverflow: But the part of that page discussing the operators seems to explain their origins as much as anyone other than Ken Thompson is likely to know. –  Feb 01 '11 at 00:32
  • 3
    @FredOverflow. I think you misunderstood exactly what the "folk myth" is. The myth is that C was designed to exploit those 11 ops, not that they don't exist and not that the code pattern didn't become popular because everyone knew they mapped to the 11 well. In case it isn't clear: the PDP-11 really really does implement those two modes in hardware for almost every instruction as an addressing mode, and it really was the first *important* machine C ran on. – DigitalRoss Feb 01 '11 at 00:32
  • 1
    I would agree that probably the single biggest reason is that the operator just makes sense. Deref the pointer and then increment it. It's exactly what you want to do in so many cases. – DigitalRoss Feb 01 '11 at 01:17
  • 1
    Declaring variables after statements has only been standard since C99, not C89. –  Feb 01 '11 at 01:50
  • @caf, that is what I was trying to say, sorry if it was unclear. *"...hasn't been required since C89"* was supposed to mean *"you have to go back to C89 to find such a requirement"*. – DigitalRoss Feb 01 '11 at 05:03
  • Ahh, I see - *"hasn't been required since C89"* sounded (to me, anyway) like the requirement was removed in C89. I've made a suggested edit to make it clearer. –  Feb 01 '11 at 06:42
  • 2
    "it must have mattered a lot on those 0.001GHz machines". Um, I hate to say it because it dates me, but I had Motorola and Intel manuals for my CPUs to look up the size of the instructions and how many cycles they took. Finding the smallest codes added up to being able to add one more feature to a print spooler and saving a couple cycles meant a serial port could keep up with some protocol like that newly invented MIDI. C relieved some of the nit-picking, especially as the compilers improved, but still it was important knowing what was the most efficient way to write code. – the Tin Man Feb 01 '11 at 06:57
24

While it did once have some performance implications, I think the real reason is for expressing your intent cleanly. The real question is whether something while (*d++=*s++); expresses intent clearly or not. IMO, it does, and I find the alternatives you offer less clear -- but that may (easily) be a result of having spent decades becoming accustomed to how things are done. Having learned C from K&R (because there were almost no other books on C at the time) probably helps too.

To an extent, it's true that terseness was valued to a much greater degree in older code. Personally, I think this was largely a good thing -- understanding a few lines of code is usually fairly trivial; what's difficult is understanding large chunks of code. Tests and studies have shown repeatedly, that fitting all the code on screen at once is a major factor in understanding the code. As screens expand, this seems to remain true, so keeping code (reasonably) terse remains valuable.

Of course it's possible to go overboard, but I don't think this is. Specifically, I think it's going overboard when understanding a single line of code becomes extremely difficult or time consuming -- specifically, when understanding fewer lines of code consumes more effort than understanding more lines. That's frequent in Lisp and APL, but doesn't seem (at least to me) to be the case here.

I'm less concerned about compiler warnings -- it's my experience that many compilers emit utterly ridiculous warnings on a fairly regular basis. While I certainly think people should understand their code (and any warnings it might produce), decent code that happens to trigger a warning in some compiler is not necessarily wrong. Admittedly, beginners don't always know what they can safely ignore, but we don't stay beginners forever, and don't need to code like we are either.

Jerry Coffin
  • 44,385
  • 5
  • 89
  • 162
  • 12
    +1 for pointing out that the terse version is *easier* to read to some of us. :-) –  Feb 01 '11 at 00:56
  • 1
    If you don't like some of your compiler warnings (and there are more than a few I could do without - seriously, I _meant_ to type `if(x = y)`, I swear!) you should adjust your compiler's warnings options so you don't accidentally miss the warnings you _do_ like. –  Feb 01 '11 at 02:09
  • @Chris Lutz: quite true -- and definitely a useful thing to do in many cases. – Jerry Coffin Feb 01 '11 at 02:16
  • Also, the newest versions of GCC will have methods to specifically annotate code to say, "Do not throw this particular warning on this particular line of code," for that reason. –  Feb 01 '11 at 02:18
  • 4
    @Brooks Moses: I'm a *lot* less enthused about that. I don't like polluting my code to make up for the compiler's shortcomings. – Jerry Coffin Feb 01 '11 at 02:23
  • @Brooks Moses - The purpose of flags like `-Wno-X` is to keep you from going through hoops (like the `if((x = y))` double parentheses) in your code to shut the compiler up. –  Feb 01 '11 at 02:33
  • 1
    @Jerry, @Chris: This annotation is intended for the case where you think the warning is usually a _good_ thing, but you don't want it to apply to a particular line that's doing something unusual. If you never want the warning and consider it a shortcoming, use `-Wno-X`, but if you only want to make one exception to it and want the warning to apply everywhere else, this is much more comprehensible than using arcane hoop-jumping such as Chris refers to. –  Feb 01 '11 at 02:50
  • 1
    @Brooks: the double parentheses and the `(void)x` to silence unused warnings are fairly well known idioms to silence otherwise useful warnings. The annotation looks a bit like `pragmas`, not portable at best :/ – Matthieu M. Feb 01 '11 at 07:28
  • You sir, just justified the purchase of ridiculous 30" monitors everywhere, and for that I thank you – bobobobo Dec 28 '11 at 13:25
  • It's worth noting that his most expanded version, the one that most explicitly expresses what the code actually does, only takes 7 lines. There's no danger of running out of screen space there, even if you're coding on a smartphone. :P – Mason Wheeler Dec 28 '11 at 18:00
  • @MasonWheeler: While true for this specific code segment, keep in mind that we're talking about a 7:1 increase in line count. If you apply it to only one line, it's irrelevant, but if you apply it in general, it's rather a different story. – Jerry Coffin Dec 28 '11 at 19:31
  • 1
    @Jerry: Fair enough. But on the other hand, two of these lines are braces, which means five lines of Actually Doing Stuff. When you've got one single line of code performing five distinct operations, IMO you're definitely getting into the Lisp/APL readability danger zone. – Mason Wheeler Dec 29 '11 at 05:47
  • IMHO, the times when the prefix/postfix distinction is helpful could conceptually be regarded as higher-level operators such as "derference and postincrement" or "decrement and check the result against zero". While I understand K&R's rationale for making individual pre/post inc/dec operators, rather than having composite operators, I think the operators make more sense as part of a composite operation. – supercat Mar 20 '15 at 06:45
16

The prefix and postfix -- and ++ operators were introduced in the B language (C's predecessor) by Ken Thompson -- and no, they were not inspired by the PDP-11, which didn't exist at the time.

Quoting from "The Development of the C Language" by Dennis Ritchie:

Thompson went a step further by inventing the ++ and -- operators, which increment or decrement; their prefix or postfix position determines whether the alteration occurs before or after noting the value of the operand. They were not in the earliest versions of B, but appeared along the way. People often guess that they were created to use the auto-increment and auto-decrement address modes provided by the DEC PDP-11 on which C and Unix first became popular. This is historically impossible, since there was no PDP-11 when B was developed. The PDP-7, however, did have a few ‘auto-increment’ memory cells, with the property that an indirect memory reference through them incremented the cell. This feature probably suggested such operators to Thompson; the generalization to make them both prefix and postfix was his own. Indeed, the auto-increment cells were not used directly in implementation of the operators, and a stronger motivation for the innovation was probably his observation that the translation of ++x was smaller than that of x=x+1.

Keith Thompson
  • 6,402
  • 2
  • 29
  • 35
  • 11
    No, I'm not related to Ken Thompson. – Keith Thompson Sep 24 '16 at 00:54
  • Thanks for this very interesting reference ! This confirms my quote from original K&R that suggested the goal of compactness rather than a technical optimization. I didn't know however that these operators already existed in B. – Christophe Sep 24 '16 at 17:30
  • @BenPen As far as I know there's no policy of closing questions if there's a duplicate on a *different* SE site, as long as both questions fit their respective sites. – Ordous Sep 26 '16 at 14:27
  • Interesting... The Programmer one is not as close a question certainly. – BenPen Sep 26 '16 at 14:33
  • @BenPen: The accepted answer to the Stack Overflow question already cites the same source I did (though not quite as much of it). – Keith Thompson Sep 26 '16 at 15:16
6

The obvious reason for the postincrement operator to exist is so that you don't have to write expressions like (++x,x-1) or (x+=1,x-1) all over the place or uselessly separate trivial statements with easy-to-understand side effects out into multiple statements. Here are some examples:

while (*s) x+=*s++-'0';

if (x) *s++='.';

Whenever reading or writing a string in the forward direction, postincrement, and not preincrement, is almost always the natural operation. I've almost never encountered real-world uses for preincrement, and the only major use I've found for predecrement is converting numbers to strings (because human writing systems write numbers backwards).

Edit: Actually it wouldn't be quite so ugly; instead of (++x,x-1) you could of course use ++x-1 or (x+=1)-1. Still x++ is more readable.

  • You have to be careful about those expressions since you are modifying and reading a variable between two sequence points. The order in which those expressions are evaluated is not guaranteed. –  Aug 07 '14 at 21:36
  • @Snowman: Which one? I see no such issues in my answer. – R.. GitHub STOP HELPING ICE Aug 07 '14 at 21:42
  • if you have something like `(++x,x-1)` (function call, maybe?) –  Aug 07 '14 at 21:46
  • @Snowman: That's not a function call. It's the C comma operator, which is a sequence point, parenthesized to control precedence. The result is the same as `x++` in most cases (one exception: `x` is an unsigned type with rank lower than `int` and the initial value of `x` is the max value of that type). – R.. GitHub STOP HELPING ICE Aug 07 '14 at 22:02
  • Ah, the tricky comma operator... when a comma is not a comma. The parenthesis and rarity of the comma operator threw me off. Nevermind! –  Aug 07 '14 at 22:03
4

The PDP-11 offered post-increment and pre-decrement operations in the instruction set. Now these weren't instructions. They were instruction modifiers that allowed you to specify that you wanted the value of a register before it was incremented or after it was decremented.

Here is a word-copy step in the machine language:

movw (r1)++,(r2)++

which moved a word from one location to another, pointed at by registers, and the registers would be ready to do it again.

As C was built on and for the PDP-11, a lot of useful concepts found their way into C. C was intended to be a useful replacement for assembler language. Pre-increment and post-decrement were added for symmetry.

BobDalgleish
  • 4,644
  • 5
  • 18
  • 23
  • That's brilliant modifiers... It makes me want to learn PDP-11 assembly. BTW, do you have a reference on "for symmetry?" It makes sense, but this is history, sometimes making sense wasn't the key. LOL – BenPen Sep 23 '16 at 21:16
  • 2
    Also known as [addressing modes](https://en.wikipedia.org/wiki/Addressing_mode). The PDP-11 provided post-increment and pre-decrement which paired nicely together for stack operations. – Erik Eidt Sep 23 '16 at 21:40
  • 1
    The PDP-8 provided a post-increment memory indirection, but no corresponding pre-decrement operation. With magnetic core memory, reading a memory word wiped it out(!), so the processor used a write-back function to restore the word just read. Applying an increment before write back came about rather naturally, and a more efficient block move became possible. – Erik Eidt Sep 23 '16 at 21:54
  • 3
    Sorry, the PDP-11 didn't inspire these operators. It didn't exist yet when Ken Thompson invented them. See [my answer](http://programmers.stackexchange.com/a/331887/33478). – Keith Thompson Sep 24 '16 at 00:54
3

I doubt it was ever really necessary. As far as I know, it doesn't compile into anything more compact on most platforms than using the pre-increment as you did, in the loop. It was just that at the time it was made, terseness of code was more important than clarity of code.

That isn't to say that clarity of code isn't (or wasn't) important, but when you were typing in on a low baud modem (anything where you measure in baud is slow) where every keystroke had to make it to the mainframe and then get echoed back one byte at a time with a single bit used as parity check, you didn't want to have to type much.

This is sort of like the & and | operator having lower precedence than ==

It was designed with the best intentions (a non-short circuiting version of && and ||), but now it confuses programmers everyday, and it probably won't ever change.

Anyway, this is only an answer in that I think there is not a good answer to your question, but I'll probably be proven wrong by a more guru coder than I.

--EDIT--

I should note that I find having both incredibly useful, I'm just pointing out that it won't change whether anyone like it or not.

  • This isn't even remotely true. At no point was "terseness of code" *more important* than clarity. – Cody Gray - on strike Feb 01 '11 at 00:10
  • Well, I would argue that it was much more of a point of focus. You're quite right though, it's certainly never been *more* important than clarity of code... to most people. –  Feb 01 '11 at 00:13
  • 6
    Well, [the definition of "terse"](http://www.merriam-webster.com/dictionary/terse) is "smoothly elegant: polished" or "using few words: devoid of superfluity," both of which are generally a good thing when writing code. "Terse" doesn't mean "obscure, hard to read, and obfuscated" like many people think. – James McNellis Feb 01 '11 at 00:21
  • @James: While you're correct, I think *most* people mean the second definition of "terse" when using it in a programming context: "using few words; devoid of superfluity". Under that definition, it's certainly easier to imagine situations where there is a trade-off between the brevity and expressiveness of a particular piece of code. Obviously the right thing to do when writing code is the same thing as when speaking: make things as short as they need to be, but no shorter. Valuing terseness over expressivity *can* lead to obfuscated-looking code, but it certainly shouldn't. – Cody Gray - on strike Feb 01 '11 at 00:41
  • 1
    rewind 40 years and imagine you had to fit your program on that drum that will hold only around 1000 characters, or write a compiler can only work with 4 thousand bytes of ram. There were times where terseness vs clarity was not even an issue. You had to be terse, there didn't exist a computer on this planet that could store, run or compile your non-terse program. – nos Feb 05 '11 at 01:38
3

I like the postfix aperator when dealing with pointers (whether or not I'm dereferencing them) because

p++

reads more naturally as "move to the next spot" than the equivalent

p += 1

which surely must confuse beginners the first time they see it used with a pointer where sizeof(*p) != 1.

Are you sure you're stating the confusion problem correctly? Is not the thing that confuses beginners the fact that the postfix ++ operator has higher precedence than the dereference * operator so that

*p++

parses as

*(p++)

and not

(*p)++

as some might expect?

(You think that's bad? See Reading C Declarations.)

Eric Giguere
  • 131
  • 2
2

Let's look at Kernighan & Ritchie original justification (original K&R page 42 and 43):

The unusual aspects is that ++ and -- may be used either as prefix or as postfix. (...) In the context where no value is wanted (..) choose prefix or postfix according to taste. But htere are situations where one or the other is specifically called for.

The text continues with some examples that use increments within index, with the explicit goal of writing "more compact" code. So the reason behind these operators is convenience of more compact code.

The three examples given (squeeze(), getline() and strcat() ) use only postfix within expressions using indexing. The authors compare the code with a longer version that doesn't use embedded increments. This confirms that focus is on compactness.

K&R highlight on page 102, the use of these operators in combination with pointer dereferencing (eg *--p and *p--). No further example is given, but again, they make clear that the benefit is compactness.

Prefix is for example very commonly used when decrementing an index or a pointer staring from the end.

 int i=0; 
 while (i<10) 
     doit (i++);  // calls function for 0 to 9  
                  // i is 10 at this stage
 while (--i >=0) 
     doit (i);    // calls function from 9 to 0 
Christophe
  • 74,672
  • 10
  • 115
  • 187
2

Amongst the subtle elements of good programming are localisation and minimalism:

  • putting variables in a minimal scope of use
  • using const when write access isn't required
  • etc.

In the same spirit, x++ can be seen as a way of localising a reference to the current value of x while immediately indicating that you've - at least for now - finished with that value and want to move to the next (valid whether x is an int or a pointer or iterator). With a little imagination, you could comparable this to letting the old value/position of x go "out of scope" immediately after it's no longer needed, and moving to the new value. The precise point where that transition is possible is highlighted by the postfix ++. The implication that this is probably the final use of the "old" x can be valuable insight into the algorithm, assisting the programmer as they scan through the surrounding code. Of course, the postfix ++ may put the programmer on the lookout for uses of the new value, which may or may not be good depending on when that new value is actually needed, so it's an aspect of the "art" or "craft" of programming to determine what's more helpful in the circumstances.

While many beginners may be confused by a feature, it's worth balancing that against the long-term benefits: beginners don't stay beginners for long.

Tony
  • 670
  • 4
  • 6
2

Wow, lots of answers not-quite-on-point (if I may be so bold), and please forgive me if I'm pointing out the obvious – particularly in light of your comment to not point out the semantics, but the obvious (from Stroustrup's perspective, I suppose) doesn't yet seem to have been posted! :)

Postfix x++ produces a temporary that's passed 'upwards' in the expression, while the the variable x is subsequently incremented.

Prefix ++x does not produce a temporary object, but increments 'x' and passes the result to the expression.

The value is convenience, for those who know the operator.

For example:

int x = 1;
foo(x++); // == foo(1)
// x == 2: true

x = 1;
foo(++x); // == foo(2)
// x == 2: true

Of course, the results of this example can be produced from other equivalent (and perhaps less oblique) code.

So why do we have the postfix operator? I guess because it's an idiom that's persisted, in spite of the confusion it obviously creates. It's a legacy construct that once was perceived to have value, though I'm not sure that perceived value was so much for performance as for convenience. That convenience hasn't been lost, but I think an appreciation of readability has increased, resulting in the questioning of the operator's purpose.

Do we need the postfix operator anymore? Likely not. Its result is confusing and creates a barrier to understandability. Some good coders will certainly immediately know where to find it useful, often in places where it has a peculiarly "PERL-esque" beauty. The cost of that beauty is readability.

I agree that explicit code has benefits over terseness, in readability, understandability and maintainability. Good designers and managers want that.

However, there's a certain beauty that some programmers recognize that encourages them to produce code with things purely for beautiful simplicity – such as the postfix operator – which code they wouldn't have otherwise ever have thought of – or found intellectually stimulating. There's a certain something to it that just makes it more beautiful, if not more desirable, in spite of pithiness.

In other words, some people find while (*dst++ = *src++); to simply be a more beautiful solution, something that takes your breath away with its simplicity, just as much as if it were a brush to canvas. That you are forced to understand the language to appreciate the beauty only adds to its magnificence.

Brian M. Hunt
  • 251
  • 1
  • 7
  • 3
    +1 "some people find while (*dst++ = *src++); to simply be a more beautiful solution". Definitely. People who don't understand assembly language don't really understand how elegant C can be, but that particular code statement is a shining star. – the Tin Man Feb 01 '11 at 06:45
  • "Do we need the postfix operator anymore? Likely not." - I can't help thinking of Turing machines here. Have we ever needed automatic variables, the `for` statement, heck, even `while` (we have `goto`, after all)? –  Feb 01 '11 at 19:37
  • @Bernd: Ha! Imagine closures! Closures! Scandalous! lol – Brian M. Hunt Feb 02 '11 at 02:40
  • Closures! How ridiculous. Just give me a tape! Seriously, what I meant is that I don't think /needing/ a feature should be the primary decision criterion (unless you want to design, say, lisp). IME I use (nay, *need*) the postfix operators almost exclusively, and only rarely need the prefix one instead. –  Feb 02 '11 at 15:14
1

this is from the POV of an electrical engineer:

there are many processors that have built-in post-increment and pre-decrement operators for the purpose of maintaining a last-in-first-out (LIFO) stack.

it might be like:

 float stack[4096];
 int stack_pointer = 0;

 ... 

 #define push_stack(arg) stack[stack_pointer++] = arg;
 #define pop_stack(arg) arg = stack[--stack_pointer];

 ...

i dunno, but that's the reason i would expect to see both prefix and postfix increment operators.

robert bristow-johnson
  • 1,190
  • 1
  • 8
  • 17
1

To underline Christophe's point about compactness, I want to show y'all some code from V6. This is part of the original C compiler, from http://minnie.tuhs.org/cgi-bin/utree.pl?file=V6/usr/source/c/c00.c:

/*
 * Look up the identifier in symbuf in the symbol table.
 * If it hashes to the same spot as a keyword, try the keyword table
 * first.  An initial "." is ignored in the hash.
 * Return is a ptr to the symbol table entry.
 */
lookup()
{
    int ihash;
    register struct hshtab *rp;
    register char *sp, *np;

    ihash = 0;
    sp = symbuf;
    if (*sp=='.')
        sp++;
    while (sp<symbuf+ncps)
        ihash =+ *sp++;
    rp = &hshtab[ihash%hshsiz];
    if (rp->hflag&FKEYW)
        if (findkw())
            return(KEYW);
    while (*(np = rp->name)) {
        for (sp=symbuf; sp<symbuf+ncps;)
            if (*np++ != *sp++)
                goto no;
        csym = rp;
        return(NAME);
    no:
        if (++rp >= &hshtab[hshsiz])
            rp = hshtab;
    }
    if(++hshused >= hshsiz) {
        error("Symbol table overflow");
        exit(1);
    }
    rp->hclass = 0;
    rp->htype = 0;
    rp->hoffset = 0;
    rp->dimp = 0;
    rp->hflag =| xdflg;
    sp = symbuf;
    for (np=rp->name; sp<symbuf+ncps;)
        *np++ = *sp++;
    csym = rp;
    return(NAME);
}

This is code that was passed around in samizdat as an education in good style, and yet we would never write it like this today. Look at how many side effects have been packed into if and while conditions, and conversely, how both for loops don't have an increment expression because that's done in the loop body. Look at how blocks are only wrapped in braces when absolutely necessary.

Part of this was certainly manual micro-optimization of the sort that we expect the compiler to do for us today, but I would also put forward the hypothesis that when your entire interface to the computer is a single 80x25 glass tty, you want your code to be as dense as possible so you can see more of it at the same time.

(Use of global buffers for everything is probably a hangover from cutting one's teeth on assembly language.)

zwol
  • 2,576
  • 1
  • 16
  • 16
  • Handle with care: "ihash =+ *sp++;" At some point C had not only += but also =+ operator. Today, the =+ is an assignment operator, followed by a unary plus which doesn't do anything, so it's equivalent to "ihash = *sp; sp += 1;" – gnasher729 Sep 28 '16 at 20:23
  • @gnasher729 Yes, and later it has `rp->hflag =| xdflg`, which I believe will be a syntax error on a modern compiler. "At some point" is precisely V6, as it happens; the changeover to `+=` form happend in V7. (The V6 compiler *only* accepted `=+`, if I'm reading this code correctly - see symbol() in the same file.) – zwol Sep 28 '16 at 20:58
1

I'm going to disagree with the premise that ++p, p++, is somehow difficult to read or unclear. One means "increment p and then read p", the other means "read p and then increment p". In both cases, the operator in the code is exactly where it is in the explanation of the code, so if you know what ++ means, you know what the resulting code means.

You can create obfuscated code using any syntax, but I don't see a case here that p++/++p is inherently obfuscatory.

philosodad
  • 1,775
  • 10
  • 14
-1

Perhaps you should think about cosmetics too.

while( i++ )

arguably "looks better" than

while( i+=1 )

For some reason, the post-increment operator looks very appealing. Its short, its sweet, and it increases everything by 1. Compare it with prefix:

while( ++i )

"looks backwards" doesn't it? You never see a plus sign FIRST in math, except when someone's being silly with specifying something's positive (+0 or something like that). We're used to seeing OBJECT + SOMETHING

Is it something to do with grading? A, A+, A++? I can tell you I was pretty cheesed at first that the Python people removed operator++ from their language!

bobobobo
  • 119
  • 1
  • 10
  • 1
    Your examples don't do the same thing. `i++` returns the original value of `i`, and `i+=1` and `++i` return the original value of `i` plus 1. Since you're using the return values for control flow, this matters. – David Thornley Dec 28 '11 at 15:23
  • Yes, you're right. But I'm looking only at readability here. – bobobobo Dec 30 '11 at 22:51
-2

Counting backwards 9 to 0 inclusive:

for (unsigned int i=10; i-- > 0;)  {
    cout << i << " ";
}

Or the equivalent while loop.

markh44
  • 101
  • 1