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?

Nemanja Boric
  • 464
  • 6
  • 15
  • Interesting question. one side note : Strings *are* arrays in Java. – Timothy Groote Mar 24 '14 at 11:49
  • 2
    "Gosling wanted the code [written by someone experienced in C](http://programmers.stackexchange.com/a/157606/31260 "see more details here") (not in Java) to be executed by someone used to running PostScript on NeWS..." – gnat Mar 24 '14 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 '14 at 11:54
  • Ok, Agreed, i won't state that again ;) – Timothy Groote Mar 24 '14 at 16:17
  • Thank you all for great answers. You've all confirmed what I was thinking, but I also learned a few new tricks today! – Nemanja Boric Mar 24 '14 at 19:06
  • 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#. – Eric Lippert Mar 24 '14 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. – Eric Lippert Mar 24 '14 at 21:08
  • Its useful if people use it. – Fresheyeball Mar 24 '14 at 21:47
  • 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 '14 at 10:56
  • 1
    Just for the record: I was "the people which made the statements" mentioned in the question. I never claimed that these operators are _useless_ in Java (or C#), I just claimed that the benefit they bring is much smaller than in C (and C++), and that, arguably, the drawbacks and caveats outweigh that benefit. I'd also like to know why exactly were they included in Java, and especially C#, which had better opportunity for hindsight. – Mladen Jablanović Mar 25 '14 at 11:30
  • @MladenJablanović (and others) Yes, that's my fault - Mladen didn't made statement that it was blindly copied - that was my interpretation (so it's kind a my assumption :)! I'm happy that he commented here, I hope for more discussion to come! – Nemanja Boric Mar 25 '14 at 11:36

5 Answers5

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.

Doc Brown
  • 199,015
  • 33
  • 367
  • 565
  • 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 '14 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. – Eric Lippert Mar 24 '14 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 '14 at 20:53
  • @DocBrown: Your answer applies pretty well to C#. I left a comment above. – Eric Lippert Mar 24 '14 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? – Mladen Jablanović Mar 27 '14 at 09: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.

Landei
  • 1,993
  • 1
  • 13
  • 19
  • 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 '14 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.

Mike Dunlavey
  • 12,815
  • 2
  • 35
  • 58
  • 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 `break`s 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 '14 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 ...` ;-) – Mike Dunlavey Mar 24 '14 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 '14 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 http://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 '14 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 '14 at 20:36
  • the person who complained about rounding was probably an accountant! Always round towards 0, then they get to keep the difference :) There are 4 different ways to round an integer - so the problem was really one of not understanding the requirements your users expected. Incidentally your 1.5=>2, -1.5=>-1 rounding is defined by the US Meteorology office in 1966. – gbjbaanb Mar 25 '14 at 08:38
  • @gbjbaanb: Of the various rounding modes, truncation is the one for which I see the least usefulness, except in cases where operands are always positive. Floor(x) and Floor(x+0.5), with a slight adjustment for 0.5-epsilon, both have the advantage that increasing x by one will increase the result by one. Round-to-nearest-even is symmetrical about 0, and increasing x by any amount between integers n and n+1 will increase the result by either n or n+1. Truncation offers neither advantage; -0.9 rounds to zero, and one can add 1.8 to it and it will still round to zero. – supercat Jul 14 '14 at 16:46
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.

gbjbaanb
  • 48,354
  • 6
  • 102
  • 172
  • 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 '18 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.

K. Eno
  • 21
  • 2