10

(Note: I am not asking about the definitions of pre-increment vs. post-increment, or how they are used in C/C++. Therefore, I do not think this is a duplicate question.)

Developers of C (Dennis Ritchie et al) created increment and decrement operators for very good reasons. What I don't understand is why they decided to create the distinction of pre- vs post- increments/decrements?

My sense is that these operators were far more useful when C was being developed than today. Most C/C++ programmers use one or the other, and programmers from other languages find the distinction today bizarre and confusing (NB: this is based solely on anecdotal evidence).

Why did they decide to do this, and what has changed in computation that this distinction isn't so useful today?

For the record, the difference between the two can be seen in C++ code:

int x = 3;

cout << "x = 3; x++ == " << x++ << endl;
cout << "++x == " << ++x << endl;
cout << "x-- == " << x-- << endl;
cout << "--x == " << --x << endl;

will give as an output

x++ == 3
++x == 5
x-- == 5
--x == 3
ShanZhengYang
  • 16,511
  • 49
  • 132
  • 234
  • 1
    [The Development of the C Language (by Ritchie)](http://cm.bell-labs.co/who/dmr/chist.html) contains a paragraph about incrementation operators as well as postfix/prefix, but doesn't go into much detail. – dyp May 25 '15 at 00:28
  • 2
    The side effect is what's useful. – Fiddling Bits May 25 '15 at 00:28
  • 1
    [Speculation warning] When doing systems programming in C or C++ you end up writing a lot of code. Anything that helps you remain succinct is good. The prefix and postfix operators allow programmers to waste less space for variable-juggling. I think this is why they added it. I am surprised that C doesn't have a variable-value-swap operator, though. – Dai May 25 '15 at 00:30
  • 2
    @Dai when I started learning C I would use any trick that looked l33t, today I respect POLA and various other principles. `Any fool can write code that a computer can understand. Good programmers write code that humans can understand. ~Martin Fowler` – v.oddou May 25 '15 at 00:37
  • @v.oddou there's a difference between being succint and being unclear or cryptic. – Dai May 25 '15 at 00:38
  • 1
    What makes you state "what has changed in computation that this distinction isn't so useful today"? For processors lacking single `inc/dec` instructions, they can trivially be replaced with `add r0,r0,#1` (ARM example). The distinction between *post* and *pre* is as important today as it ever was. – Jongware May 25 '15 at 01:05
  • My theory it has to do with *stack management*. To push onto a *stack* you need *increment after* and to pop that element you need *decrement before*. – Galik May 25 '15 at 02:07
  • @ShanZhengYang have you any statistics - say backed by an analysis of a group of open source projects or of programmers - to back the presumptions in this question? And calling a variable `three` rather than say `x` when you plan to mutate it seems a deliberate effort to portray these operators as confusing. – Tony Delroy May 25 '15 at 13:57
  • @TonyD You're welcome to edit the code. It is fairly clear in my book, but you're the reader. Also, this is an internet discussion, not a publication. There's no need for me to conduct a multimillion dollar survey in order to present how programmers think of pre- vs post-. I am basing this solely on personal experience, personal conversations, and the fact that StackExchange has dozens of posts discussing this. Also, it would be detrimental if I posted questions if and if only I had substantial statistics which you recommend. That is not how STEMers discuss problems amongst themselves. – ShanZhengYang May 25 '15 at 15:40
  • @ShanZhengYang: code edited; appreciate your note re "anecdotal evidence". Cheers. – Tony Delroy May 26 '15 at 01:02
  • Related: http://programmers.stackexchange.com/q/331870/33478 – Keith Thompson Sep 26 '16 at 15:17

6 Answers6

10

Incrementing and decrementing by 1 were widely supported in hardware at the time: a single opcode, and fast. This because "incrementing by 1" and "decrementing by 1" were a very common operation in code (true to this day).

The post- and predecrement forms only affected the place where this opcode got inserted in the generated machine code. Conceptually, this mimics "increase/decrease before or after using the result". In a single statement

i++;

the 'before/after' concept is not used (and so it does the same as ++i;), but in

printf ("%d", ++i);

it is. That distinction is as important nowadays as it was when the language C was designed (this particular idiom was copied from its precursor named "B").

From The Development of the C Language

This feature [PDP-7's "`auto-increment' memory cells"] probably suggested such operators to Thompson [Ken Thompson, who designed "B", the precursor of C]; 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.

Thanks to @dyp for mentioning this document.

Jongware
  • 22,200
  • 8
  • 54
  • 100
  • 6
    Ritchie, The Development of the C Language: *"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 seems to have some features that might have played a role, albeit I don't think it's entirely clear from the document that they were the main reason for the existence of both prefix and postfix `++`. – dyp May 25 '15 at 00:36
  • @dyp: true as that may be, I don't believe they invented it "out of nothing". An instruction `inc X` may have existed before the PDP-11. – Jongware May 25 '15 at 00:39
  • I have revised my comment. Performance might have been one aspect, but I don't think opcodes can show the whole picture. – dyp May 25 '15 at 00:40
  • @dyp: wonderful read. I included the relevant quote as it seems totally appropriate. – Jongware May 25 '15 at 00:48
  • 2
    `the translation was smaller`. so here you have your hysterical raisin : **lack of an optimizer in early compilers**. – v.oddou May 25 '15 at 03:58
  • 1
    B ["actively" ran on a Honeywell 6070](https://www.bell-labs.com/usr/dmr/www/btut.pdf), which had an [`AOS` (add one to storage) instruction](http://www.trailingedge.com/misc/GCOS-GMAP-PocketGuide.pdf) circa at least 1967. It did not have not a similar subtract operation, and [neither did the PDP-7](http://bitsavers.trailing-edge.com/pdf/dec/pdp7/F-75P_PDP7prelimUM_Dec64.pdf) where [B went next](http://history-computer.com/ModernComputer/Software/Unix.html). By the time C hit a PDP-11, the DEC team had introduced [auto-addressing modes](http://www.cdf.toronto.edu/~ajr/258/pdp11.pdf). – bishop Sep 25 '16 at 18:04
  • 1
    Given that add one to storage was a "thing" as early as 1967, and full "addressing modes" were available after about 1970, I suspect there was general community recognition of the value of these operations at both a low level (instruction sets) and at a high level (B, C). – bishop Sep 25 '16 at 18:06
  • 1
    [This related answer](http://programmers.stackexchange.com/a/331887/33478) has a longer excerpt from "The Development of the C Language". – Keith Thompson Sep 26 '16 at 15:15
5

When you count down from n it is very important whether is pre-decrement or post-decrement

#include <stdio.h>
void foopre(int n) {
    printf("pre");
    while (--n) printf(" %d", n);
    puts("");
}
void foopost(int n) {
    printf("post");
    while (n--) printf(" %d", n);
    puts("");
}
int main(void) {
    foopre(5);
    foopost(5);
    return 0;
}

See the code running at ideone.

pmg
  • 106,608
  • 13
  • 126
  • 198
  • Thanks for sharing this wisdom. I added a reference to your post. Most probably my assumption the difference between both versons is obvios was wrong and it somehow got overred in my answer. – mikyra May 25 '15 at 14:00
2

To get an answer that goes beyond speculation, most probably you have to ask Dennis Ritchie et al personally.

Adding to the answer already given, I'd like to add two possible reasons I came up with:

  • lazyness / conserving space:

    you might be able to save a few keystrokes / bytes in the input file using the appropriate version in constructs like while(--i) vs. while(i--). (take a look at pmg s answer to see, why both make a difference, if you didn't see it in the first run)

  • esthetics

    For reasons of symmetry having just one version either pre- or postincrement / decrement might feel like missing something.

EDIT: added sparing a few bytes in the input file in the speculation section providing, now providing a pretty nice "historic" reason as well.

Anyways the main point in putting together the list was giving examples of possible explanations not being too historic, but still holding today.

Of course I am not sure, but I think asking for a "historic" reason other than personal taste is starting from a presumtion not neccesarily true.

mikyra
  • 10,077
  • 1
  • 40
  • 41
  • 3
    Sadly, to ask Dennis, you'd need a Ouija board -- he passed away 4 years ago. – Charlie Martin May 25 '15 at 00:53
  • 2
    mikyra, hand in your nerd badge. Next you'll be telling us you didn't know about Leonard Nimoy :-) – paxdiablo May 25 '15 at 01:58
  • 1
    And John Nash... (But at the time of creating C (well, B actually) "laziness" may not have been a point but saving a few precious bytes in the *input* file may have been.) – Jongware May 25 '15 at 11:47
  • ... what about Elvis? Don't tell me ... good point anyways I will add that to the speculations. – mikyra May 25 '15 at 13:43
  • ... anyways the main point was demonstiating reasons might not be tooo historic, as for both reasons given, I would still miss the feature today and rather recognize a language as strange that doesn't support them. – mikyra May 25 '15 at 13:47
2

For C

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.

For C++

Bjarne Stroustrup wanted to have C compatibility, so C++ inherited prefix and postfix increment and decrement.

But there's more on it: in his book "The design and evolution of C++", Stroustrup explains that initially, he planned have only one overload for both, postfix and prefix, in user defined classes:

Several people, notably Brian Kernighan, pointed out that this restriction was unnatural from a C perspective and prevented users from defining a class that could be used as replacement for an ordinary pointer.

Which caused him to find the current signature difference to differentiate prefix and postfix.

By the way, without these operators C++ would not be C++ but C_plus_1 ;-)

Christophe
  • 68,716
  • 7
  • 72
  • 138
1

Consider the following loop:

for(uint i=5; i-- > 0;)
{
    //do something with i,
    // e.g. call a function that _requires_ an unsigned parameter.
}

You can't replicate this loop with a pre-decrement operation without moving the decrement operation outside of the for(...) construct, and it's just better to have your initialization, interation and check all in one place.

A much larger issue is this: one can over-load the increment operators (all 4) for a class. But then the operators are critically different: the post operators usually result in a temporary copy of the class instance being made, where as the pre-operators do not. That is a huge difference in semantics.

David I. McIntosh
  • 2,038
  • 4
  • 23
  • 45
0

The PDP-11 had a single instruction that corresponded to *p++, and another for *--p (or possibly the other way round).

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Please see [The Development of the C Language (by Ritchie)](http://cm.bell-labs.co/who/dmr/chist.html). Those operators were in B, which was pre PDP-11. – dyp May 25 '15 at 00:39