8

Recently, I've stumbled across question about predicting the output of code which heavily uses post/pre increment operators on integers. I am experienced C programmer, so I felt like at home, but people made statements that Java just blindly copied that from C (or C++), and that this is an useless feature in Java.

That being said, I am not able to find good reason for that (especially with keeping in mind the difference betwen post/pre forms) in Java (or C#), because it's not like you will manipulate arrays and use them as strings in Java. Also, it was long ago when I last looked into bytecode, so I don't know if there's a INC operation, but I don't see any reason why this

for(int i = 0; i < n; i += 1)

could be less effective than

for(int i = 0; i < n; i++)

Is there any particular part of the language where this is really useful, or is this just a feature to bring C programmers in town?

11
  • 2
    "Gosling wanted the code written by someone experienced in C (not in Java) to be executed by someone used to running PostScript on NeWS..."
    – gnat
    Mar 24, 2014 at 11:52
  • 7
    @TimothyGroote I don't think that's a fair assessment since 1) arrays are mutable, Strings aren't; 2) you can't assign a String to a char[] or vice-versa. They're certainly array-like, they're likely implemented with arrays, and it's easy enough to convert to and from an array, but they're definitely not the same thing.
    – Doval
    Mar 24, 2014 at 11:54
  • 4
    Lots of good answers here so I won't re-state what others have already said, except to emphasize that these operators are horrid features. They're very confusing; after over 25 years I still get pre- and post- semantics mixed up. They encourage bad habits like combining evaluation of results with production of side effects. Had these features not been in C/C++/Java/JavaScript/etc, they would not have been invented for C#. Mar 24, 2014 at 20:39
  • 5
    To respond directly to the claims made by your people: C# did not blindly copy anything from anyone. Every feature of C# was extremely carefully designed. And these are not useless features; they have a use. Mar 24, 2014 at 21:08
  • 2
    @EricLippert: I think (hope) most of us can agree that pre/post increment are awful, but in that context... why were they included in C#? For familiarity's sake?
    – Phoshi
    Mar 25, 2014 at 10:56

5 Answers 5

19

is this just a feature to bring C programmers in town

It is surely a feature "to bring C (or C++) programmers in town", but using the word "just" here underestimates the value of such a similarity.

  • it makes code (or at least code snippets) easier to port from C (or C++) to Java or C#

  • i++ is less to type than i += 1, and x[i++]=0; is much more idiomatic than x[i]=0;i+=1;

And yes, code which heavily uses post/pre increment operators maybe hard to read and maintain, but for any code where these operators are misused, I would not expect a drastic increase in code quality even when the language would not provide these operators. That's because if you have devs which don't care for maintainability, they will always find ways to write hard-to-understand code.

Related: Is there any difference between the Java and C++ operators? From this answer one can deduce that any well-defined operator behaviour in C is similar in Java, and Java does only add some definitions for operator precedence where C has undefined behaviour. This does not look like coincidence, it seems to be pretty intentional.

5
  • 10
    "if you have devs which don't care for maintainability, they will always find ways to write hard-to-understand code" - very well said
    – Lovis
    Mar 24, 2014 at 12:53
  • Do you subscribe to the common belief among C programmers that you only have so many C keystrokes in you before you die? Saving that one keystroke seems like a poor tradeoff to me. Mar 24, 2014 at 20:41
  • @EricLippert: hello Eric, as one of the creators of C# why don't you enlighten us and tell us a little bit about your motivation to keep the operators in C# similar to Java and C?
    – Doc Brown
    Mar 24, 2014 at 20:53
  • @DocBrown: Your answer applies pretty well to C#. I left a comment above. Mar 24, 2014 at 20:54
  • 2
    @DocBrown: x[i++]=0 is idiomatic in C, but I disagree that it's idiomatic in C# and Java. How often you encounter such phrases, unless you work in engineering/mathematics domain, which, I presume, consitutes no more than 1% of Java/C# code in the wild? Mar 27, 2014 at 9:38
6

I use sometimes the postfix increment in return statements. E.g. consider this:

//Java
public class ArrayIterator implements Iterator<T> {
   private final T[] array;
   private int index = 0; 
   public ArrayIterator(T ... array) {
      this.array = array;
   }

   public T next {
      if (! hasNext()) throw new NoSuchElementException();
      return array[index++];   
   }
   ...
}

You can't do this with index += 1. So at least the postfix version can save you now and then some lines.

That said, these operators are indeed not necessary, and may confuse people. I know Scala decided against these operators (you have only += and -=, as you suggested), and due to the more "high-level" approach I don't really miss them there. I hope Java develops (more slowly) in the same direction.

However Java has a bunch of other operators you don't see very often "in the wild" (did you ever use ^= or >>>= ?), and nobody cares.

1
  • 1
    ^= is a reasonably common one. You can use it for flipping bitfield flags, swapping two integers without a temporary variable, and in some hash/cipher algorithms. I admit that I've never used >>>=, though. (Then again, I've never used >>> or <<< in any production program...)
    – Brian S
    Mar 24, 2014 at 21:40
4

I've built many languages over the years, mostly special-purpose. What happens is you have a large audience, and they all have expectations. In considering each feature, you have to weigh the benefit of the feature against the cost of possibly violating expectations.

One that I personally had to backpedal on was integer division, which normally truncates downward, right? Like 3 / 2 = 1.5 = 1, right? Well, way back in the early days of Fortran, somebody decided -3 / 2 = -1.5 = -1 (not -2). In other words, if the dividend is negative and the divisor is positive, it should truncate up. Note what this implies - in that case the remainder is negative. That violates the usual assumption that remainder = modulus. If you're doing graphics, with integer coordinates, this can lead to all kinds of trouble. SO, in the language I simply had integer division truncate downward all the time, and say so in the doc.

And SO, guess what happened? All h*** broke loose. The language had a bug in integer division!

It did no good to say the old way was broken. It was easier to change it than to argue it.

So to answer your question, in languages like C# and Java, it's simply easier to include the ++ operators (which I personally like) than to explain why not.

7
  • 4
    As another example, the D programming language (which is in many ways the next evolutionary step from C++) has a switch statement, and they kept the required breaks in each clause even though the language doesn't allow falling through. While D threw away C++'s backwards compatibility with C, their policy is that any C construct that D kept should surprise a C programmer as little as possible.
    – Doval
    Mar 24, 2014 at 13:07
  • @Doval: I even built a language I called D, like C but with OO and parallelism features. It went nowhere (mercifully) and has no relation to the D you are talking about. I drive people crazy in C?? by always writing break; case ... ;-) Mar 24, 2014 at 13:16
  • 1
    @MikeDunlavey: My preference would be to say that / yields double or float (using double in cases where either would work), and have separate operators for floored, truncated, and "do whatever" division, as well as modulus, remainder, and "evenly divisible by" [the latter yielding a Boolean]. I would posit that any result of int x=y/z; has a significant likelihood of differing from what was intended, so therefore the expression shouldn't yield any result.
    – supercat
    Mar 24, 2014 at 19:40
  • It's not uncommon in languages to have separate operators for int-divide-by-int yielding int or floating point. Also, the sign of the remainder (affects the quotient) varies wildly. See en.wikipedia.org/wiki/Modulo_operation for a list of languages. Part of the decision for FORTRAN was undoubtedly based on what the hardware they used (IBM pre-360?) and how it behaved with signed dividend and/or divisor.
    – Phil Perry
    Mar 24, 2014 at 19:52
  • I know both Pascal and Python separate out the "yielding int" from "yielding floating-point" operators, though I've not seen any which separate out the rounding modes. I find it really icky that code which wants to e.g. compute the average of three numbers and could in Python be written as (a+b+c)//3 must in most languages be written as some obnoxious mess in order to deal with truncated-division semantics (especially since, on many processors, a floored-divide-by-three could be done faster than a truncated one!)
    – supercat
    Mar 24, 2014 at 20:36
2

Yup, its nothing important in itself, its only benefit is the syntactic sugar that makes it easier to type.

The reason it is in C in the first place is solely because the PDP-11 that was the basis for C, way back in the old days, had a special instruction for incrementing by 1. In those days, using this instruction was important to get more speed out of the system, hence the language feature.

However, I think people like the command so much they would complain if it wasn't there.

1
  • Just for the sake of accuracy, it's not so much that the PDP-11 had an instruction for pre- or post-incrementing, but had addressing modes for them (and decrementing), meaning that any instruction that accessed memory through a register could also be set to either pre- or post-increment or decrement that register at the same time. Compiler technology at the time really wasn't up to doing that automatically through optimization, so explicit support was the only way to get it to work.
    – Jules
    Feb 22, 2018 at 16:01
2

This is idiomatic syntax in C for many situations (such as the for loop you cited), so as others have said, it can help those transitioning to Java as a new language and makes it easier to port pre-existing C-style code. For those reasons, including it was a natural decision to make in Java's early days to speed adoption of the new language. It makes less sense now, because the more compact code comes at the expense of requiring programmers to learn syntax that isn't strictly necessary.

Those who like it find it both convenient and natural. Doing without it seems awkward. Using it gains no efficiency, it's a matter of readability. If you think your programs are easier to read and maintain using these operators, use them.

The original sense in C was more than just "add 1" or "subtract 1". It's much more than just syntactic sugar or small performance gains. The operator in C means "increment (or decrement) to the next item". If you have a pointer to something larger than one byte, and you step to the next address as in *pointer++, the actual increment might be by 4, or 8, or whatever is required. The compiler does the work for you, keeping track of the size of the data items and skipping the right number of bytes:

int k;
int *kpointer = &k;
*kpointer++ = 7;

On my machine, this adds 4 to kpointer, it does not add 1. This is a real help in C, preventing errors and saving spurious invocations of sizeof(). There is no equivalent pointer arithmetic in Java, so this isn't a consideration. There, it's more about the power of expression than anything else.

Not the answer you're looking for? Browse other questions tagged or ask your own question.