6

What is the necessity of both prefix and postfix increment operators? Is not one enough?

To the point, there exists like a similar while/do-while necessity problem, yet, there is not so much confusion (in understanding and usage) in having them both. But with having both prefix and postfix (like priority of these operators, their association, usage, working). And do anyone been through a situation where you said "Hey, I am going to use postfix increment. Its useful here."

16
  • 3
    @Dayalrai - That question addresses why prefix (Polish) and postfix (RPN) mathematical notation are useful, not the usefulness of both types of increment operator. I wouldn't consider that the same question at all. Commented Jul 2, 2013 at 16:53
  • 2
    There's no necessity of either. But rather than in loop control, the difference and usefulness of postfix/prefix can be seen in implementations of stack; push/pop are operators that must have the opposite logic. Commented Jul 2, 2013 at 17:02
  • 5
    We don't need these operators at all. Some languages lack them completely yet they're Turing-complete. They're just for convenience. Commented Jul 2, 2013 at 17:13
  • 1
    @Karthiprime Yes. They're short - easier to read and write, so they're convenient to use. if (++i > 0) looks certainly better than if ((i = i + 1) > 0)... Commented Jul 2, 2013 at 17:30
  • 3
    (It should be noted that the original functions mapped rather closely to instructions on the DEC computer (DEC 11?) which was the original target of C. Achieving efficient translation of the language to machine code was considered important, as was achieving easy compilation. Without those motivations the functions would likely never have been invented.) Commented Jul 2, 2013 at 17:30

8 Answers 8

5

POSTFIX and PREFIX are not the same. POSTFIX increments/decrements only after the current statement/instruction is over. Whereas PREFIX increments/decrements and then executes the current step. Example, To run a loop n times,

while(n--) { } 

works perfectly. But,

while(--n) { } 

will run only n-1 times

Or for example:

x = n--; different then x = --n; (in second form value of x and n will be same). Off-course we can do same thing with binary operator - in multiple steps.

Point is suppose if there is only post -- then we have to write x = --n in two steps.

There can be other better reasons, But this is one I suppose a benefit to keep both prefix and postfix operator.

Sign up to request clarification or add additional context in comments.

7 Comments

+ simple answer, both are different thats what both are needed.
And, of course, you need the postfix operator to be able to write while(n --> 0) :-)
yes. why not. let's add a k--=n operation. you can use it, therefore you need it. This logic is flawed.
-1 The OP did not ask what the operators did, but why we needed both of them. These two loops are hardly the norm for how to write standard counting loops, so they prove nothing. The standard way to do it would have been for(i=0; i<n; i++), which happens to be 100% equivalent to for(i=0; i<n; ++i).
@Lundin may be you have better reason to keep both pre and post ++, --, But I think what Aswin Murugesh wanted to say is one valid reason. (actually answer don't misguide instead help at once so I think answer shouldn't get downvotes)
|
3

[edit to answer OP's first part]

Clearly i++ and ++i both affect i the same but return different values. The operations are different. Thus much code takes advantage of these differences.

The most obvious need to have both operators is the 40 year code base for C. Once a feature in a language is used extensively, very difficult to remove.

Certainly a new language could be defined with only one or none. But will it play in Peoria? We could get rid of the - operator too, just use a + -b, but I think it is a tough sell.

Need both?

The prefix operator is easy to mimic with alternate code for ++i is pretty much the same as i += 1. Other than operator precedence, which parens solves, I see no difference.

The postfix operator is cumbersome to mimic - as in this failed attempt if(i++) vs. if(i += 1).

If C of the future moved to depreciate one of these, I suspect it would be to depreciate the prefix operator for its functionality, as discussed above, is easier to replace.

Forward looking thought: the >> and << operators were appropriated in C++ to do something quite different from integer bit shifting. Maybe the ++pre and post++ will generate expanded meaning in another language.

[Original follows]

Answer to the trailing OP question "do anyone been through a situation where you saidd "Hey, I am going to use postfix increment. Its useful here"?

Various array processing, like with char[], benefit. Array indexing, starting at 0, lends itself to a postfix increment. For after fetching/setting the array element, the only thing to do with the index before the next array access is to increment the index. Might as well do so immediately.

With prefix increment, one may need to have one type of fetch for the 0th element and another type of fetch for the rest.

size_t j = 0; 

for (size_t i = 0, (ch = inbuffer[i]) != '\0'; i++) { if (condition(ch)) { outbuffer[j++] = ch; // prefer this over below } } outbuffer[j] = '\0'; 

vs.

for (size_t i = 0, (ch = inbuffer[i]) != '\0'; ++i) { if (condition(ch)) { outbuffer[j] = ch; ++j; } } outbuffer[j] = '\0'; 

5 Comments

"prefer this over below"... why? That is just a subjective opinion with no rationale. But suppose I need to go in and maintain your code by adding a ch+j to it: outbuffer[j++] = ch + j;. Oops, undefined behavior, crash & burn. Now suppose I maintain the other example similarly: outbuffer[j] = ch + j;. Well-defined behavior and it does what I intended.
Sorry you did not find the rationale in the text body. Capable compilers that yield the the same machine code for y[j++] = x and y[j] = x; j++ are also capable of warning about y[j++] = x+j.
Regarding warnings against y[j++] = x+j, there are no guarantees that the compiler warns. It is undefined behavior, so it is the programmer's responsibility to keep track of it, not the compiler. A certain compiler may have implemented some own behavior for it, like old versions of GCC, which used to launch a computer game each time undefined behavior was invoked, hinting that the programmer is the one to blame for it. gcc -std=c99 -pedantic will not warn for this. You have to use -Wall or -Wsequence-point. I think very few compilers will warn for it.
Regarding efficiency, I'm pretty certain that all compilers ever made generates the same machine code for those two cases, without optimizations enabled. They need not be "capable" compilers, half-arsed ones will do. If you believe the former version is more efficient, I'd say you are confused about how machine code is generated from C source. Both cases will have machine code that looks something like: 1) Load y into index register, 2) add index register and j, store result in index register, 3) Store x into the location where the index register points at, 4) increment j.
@Lundin At least GCC can detect y[j++] = x+j. Thank-you for pointing that out and how to enable it. BTW: 68xxx processors have a Address with post-increment instruction and so do not follow the 4 steps outlined - thought likely generate same code for the 2 cases. Suspect you'll want the LW.
1

I think the only fair answer to which one to keep would be to do away with them both.

If, for example, you were to do away with postfix operators, then where code was once compactly expressed using n++, you would now have to refer to (++n - 1), or you would have to rearrange other terms.

If you broke the increment or decrement out onto its own line before or after the expression which referred to n, above, then it's not really relevant which you use, but in that case you could just as easily use neither, and replace that line with n = n + 1;

So perhaps the real issue, here, is expressions with side effects. If you like compact code then you'll see that both pre and post are necessary for different situations. Otherwise there doesn't seem to be much point in keeping either of them.

Example usage of each:

char array[10]; char * const end = array + sizeof(array) / sizeof(*array); char *p = end; int i = 0; /* set array to { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 } */ while (p > array) *--p = i++; p = array; i = 0; /* set array to { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } */ while (p < end) *p++ = i++; 

Comments

1

They are necessary because they are already used in lots of code, so if they were removed then lots of code would fail to compile.

As to why they ever existed in the first place, older compilers could generate more efficient code for ++i and i++ than they could for i+=1 and (i+=1)-1. For newer compilers this is generally not an issue.

The postfix version is something of an anomaly, as nowhere else in C is there an operator that modifies its operand but evaluates to the prior value of its operand.

One could certainly get by using only one or other of prefix or postfix increment operators. It would be a little more difficult to get by using only one or other of while or do while, as the difference between them is greater than the difference between prefix and postfix increment in my view.

And one could of course get by without using either prefix or postfix increment, or while or do while. But where do you draw the line between what's needless cruft and what's useful abstraction?

Comments

1

Here's a quickie example that uses both; an array-based stack, where the stack grows towards 0:

#define STACKSIZE ... typedef ... T; T stack[STACKSIZE]; size_t stackptr = STACKSIZE; // push operation if ( stackptr ) stack[ --stackptr ] = value; // pop operation if ( stackptr < STACKSIZE ) value = stack[ stackptr++ ]; 

Now we could accomplish the exact same thing without the ++ and -- operators, but it wouldn't scan as cleanly.

2 Comments

Yes! - the stack pointer asymmetric pre-decrement and post increment. Likely the reason for early processors to first have such optimized built-in instructions. Then once a processor has them, C programmers wanted easy access to that functionality too.
if (stackptr != 0) { stackptr--; stack[stackptr] = value; }. Yields the same machine code. if ( stackptr < STACKSIZE ) { value = stack[stackptr]; stackptr++; }. Yields the same machine code. The difference is that my versions are, in my opinion, more readable (and also MISRA-C compatible).
1

As for any other obscure mechanism in the C language, there are various historical reasons for it. In ancient times when dinosaurs walked the earth, compilers would make more efficient code out of i++ than i+=1. In some cases, compilers would generate less efficient code for i++ than for ++i, because i++ needed to save away the value to increment later. Unless you have a dinosaur compiler, none of this matters the slightest in terms of efficiency.

As for any other obscure mechanism in the C language, if it exists, people will start to use it. I'll use the common expression *p++ as an example (it means: p is a pointer, take the contents of p, use that as the result of the expression, then increment the pointer). It must use postfix and never prefix, or it would mean something completely different.

Some dinosaur once started writing needlessly complex expressions such as the *p++ and because they did, it has became common and today we regard such code as something trivial. Not because it is, but because we are so used at reading it.

But in modern programming, there is absolutely no reason to ever write *p++. For example, if we look at the implementation of the memcpy function, which has these prerequisites:

void* memcpy (void* restrict s1, const void* restrict s2, size_t n) { uint8_t* p1 = (uint8_t*)s1; const uint8_t* p2 = (const uint8_t*)s2; 

Then one popular way to implement the actual copying is:

while(n--) { *p1++ = *p2++; } 

Now some people will cheer, because we used so few lines of code. But few lines of code is not necessarily a measure of good code. Often it is the opposite: consider replacing it with a single line while(n--)*p1++=*p2++; and you see why this is true.

I don't think either case is very readable, you have to be a somewhat experienced C programmer to grasp it without scratching your head for five minutes. And you could write the same code like this:

while(n != 0) { *p1 = *p2; p1++; p2++; n--; } 

Far clearer, and most importantly it yields exactly the same machine code as the first example.

And now see what happened: because we decided not to write obscure code with lots of operands in one expression, we might as well have used ++p1 and ++p2. It would give the same machine code. Prefix or postfix does not matter. But in the first example with obscure code, *++p1 = *++p2 would have completely changed the meaning.

To sum it up:

  • There exist prefix and postfix increment operators for historical reasons.
  • In modern programming, having two different such operators is completely superfluous, unless you write obscure code with several operators in the same expression.
  • If you write obscure code, will find ways to motivate the use of both prefix and postfix. However, all such code can always be rewritten.

You can use this as a quality measure of your code: if you ever find yourself writing code where it matters whether you are using prefix or postfix, you are writing bad code. Stop it, rewrite the code.

Comments

0

Prefix operator first increments value then its uses in the expression. Postfix operator,first uses the value in the expression and increments the value

The basic use of prefix/postfix operators are assembler replaces it with single increment/decrement instruction. If we use arithmetic operators instead of increment or decrement operators, assembler replaces it with two or three instructions. that's why we use increment/decrement operators.

5 Comments

I think most compilers are smart enough to replace i += 1; with i++;
@Scintillo using replace i += 1; with i++ would not be recommended as in while(i += 1) vs. while(i++).
@chux If you're using the returned value from the expression then i++ won't translate to only one assembly instruction anyway.
It was true many years ago. not today. You don't write x>>1 instead of x/2 (I hope).
@Leushenko I doubt the translation in defined in C and it does translate to 1 instruction in PICs
0

You don't need both.

It is useful for implementing a stack, so it exists in some machine languages. From there it has been inherited indirectly to C (In which this redundancy is still somewhat useful, and some C programmers seems to like the idea of combining two unrelated operations in a single expression), and from C to any other C-like lagnuages.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.