I++ vs i+=1



  • @accalia said:

    >Groaner:
    Except you can overload ++, --, and many other operators in C# as well.

    DO NOT WANT

    That was my exact reaction on first exposure to cout << "Hello world!"

    Gratuitous overload of bit-shift to mean something completely and utterly different was just Wrong From The Start.
    https://www.youtube.com/watch?v=jeg3ZPEm9Ns&t=17s



  • @flabdablet said:

    cout << "Hello world!"

    I think it's reasonable to say that there were a lot of missteps during the early formation of C++ and its standard libraries. (Why is this not a reference? Why are new and delete special syntax rather than template functions? Why is it always more impactful to ask a third question rather than just two?)



  • Don't forget to rate-limit the banning of all the things.



  • BITCH! COMPLAIN! you cannot ban self because you have already banned self. Please try to ban self again later...


  • BINNED

    @Masaaki_Hosoi said:

    ooog oog ug oog ook ook!

    Ook?



  • @Onyx said:

    Ook?

    I knew somebody was going to bring that up. I just knew it.


  • BINNED

    @Masaaki_Hosoi said:

    I knew somebody was going to bring that up. I just knew it.

    It is my sworn duty to make the most obvious joke first so that the creativity of other members may flourish without that burden.



  • @Onyx said:

    It is my sworn duty to make the most obvious joke first so that the creativity of other members may flourish without that burden.

    That's what she said?


  • BINNED

    @tar said:

    That's what she said?

    Well, she was a COMPLAIN...



  • In response to the original question, I had a suspicion that there might be a difference when it comes to thread safety between i++ and i+=1, so I ran a brief test.

    TL;DR: It doesn't.

    Source:

    #include <stdio.h>

    void testFromRegister(int a, int b) {
    a++;
    b+=1;
    printf("%d%d",a,b);
    }

    void testFromMemory(int junk, int rubbish, int sod, int areWeThereYet, int almost, int howAboutNow, int a, int b) {
    junk=rubbish=sod=areWeThereYet=almost=howAboutNow=5;
    a++;
    b+=1;
    printf("%d%d",a,b);
    }

    int main(void) {
    testFromRegister(10,20);
    testFromMemory(1,2,3,4,5,6,30,40);
    }

    Compiled with: g++ -S -fverbose-asm -g test.c

    Relevant parts of the output:

    Lfunc_begin0:
    .loc 1 4 0 ## test.c:4:0
    .cfi_startproc

    BB#0:

    pushq	%rbp
    

    Ltmp2:
    .cfi_def_cfa_offset 16
    Ltmp3:
    .cfi_offset %rbp, -16
    movq %rsp, %rbp
    Ltmp4:
    .cfi_def_cfa_register %rbp
    subq $16, %rsp
    leaq L_.str(%rip), %rax
    movl %edi, -4(%rbp)
    movl %esi, -8(%rbp)
    .loc 1 5 0 prologue_end ## test.c:5:0
    Ltmp5:
    movl -4(%rbp), %esi
    addl $1, %esi
    movl %esi, -4(%rbp)
    .loc 1 6 0 ## test.c:6:0
    movl -8(%rbp), %esi
    addl $1, %esi
    movl %esi, -8(%rbp)
    .loc 1 7 0 ## test.c:7:0
    movl -4(%rbp), %esi
    movl -8(%rbp), %edx
    movq %rax, %rdi
    movb $0, %al
    callq _printf
    .loc 1 8 0 ## test.c:8:0
    movl %eax, -12(%rbp) ## 4-byte Spill
    addq $16, %rsp
    popq %rbp
    retq
    Ltmp6:
    Lfunc_end0:
    .cfi_endproc

    .globl __Z14testFromMemoryiiiiiiii
    .align 4, 0x90
    __Z14testFromMemoryiiiiiiii: ## @_Z14testFromMemoryiiiiiiii
    Lfunc_begin1:
    .loc 1 11 0 ## test.c:11:0
    .cfi_startproc

    BB#0:

    pushq	%rbp
    

    Ltmp9:
    .cfi_def_cfa_offset 16
    Ltmp10:
    .cfi_offset %rbp, -16
    movq %rsp, %rbp
    Ltmp11:
    .cfi_def_cfa_register %rbp
    subq $48, %rsp
    movl 24(%rbp), %eax
    movl 16(%rbp), %r10d
    leaq L_.str(%rip), %r11
    movl %edi, -4(%rbp)
    movl %esi, -8(%rbp)
    movl %edx, -12(%rbp)
    movl %ecx, -16(%rbp)
    movl %r8d, -20(%rbp)
    movl %r9d, -24(%rbp)
    movl %r10d, -28(%rbp)
    movl %eax, -32(%rbp)
    .loc 1 12 0 prologue_end ## test.c:12:0
    Ltmp12:
    movl $5, -24(%rbp)
    movl $5, -20(%rbp)
    movl $5, -16(%rbp)
    movl $5, -12(%rbp)
    movl $5, -8(%rbp)
    movl $5, -4(%rbp)
    .loc 1 13 0 ## test.c:13:0
    movl -28(%rbp), %eax
    addl $1, %eax
    movl %eax, -28(%rbp)
    .loc 1 14 0 ## test.c:14:0
    movl -32(%rbp), %eax
    addl $1, %eax
    movl %eax, -32(%rbp)
    .loc 1 15 0 ## test.c:15:0
    movl -28(%rbp), %esi
    movl -32(%rbp), %edx
    movq %r11, %rdi
    movb $0, %al
    callq _printf
    .loc 1 16 0 ## test.c:16:0
    movl %eax, -36(%rbp) ## 4-byte Spill
    addq $48, %rsp
    popq %rbp
    retq
    Ltmp13:
    Lfunc_end1:
    .cfi_endproc


    $\sum$ Both operations are translated to the same set of operations:

    movl -28(%rbp), %eax addl $1, %eax movl %eax, -28(%rbp)

    (move from stack to register, add the literal $1, and move back to the stack)

    Caveats: No -O flag. I tried with -O2, but the effect is the same, it's just so much more difficult to read.



  • To my satisfaction, I was able to use a similar technique to demonstrate to a C++ language pedant that for(int i = 0; i < n; i++) and for(int i = 0; i < n; ++i) produce identical code, which allowed my i++ to stand code review.



  • @ben_lubar said:

    They're only atomic if you don't use the same memory twice in a statement. i++ is not atomic in any language I know of. It's not even atomic in assembly. You need to use special instructions like "compare and set" to get atomic operations.

    lock addl %0, %1?


  • BINNED

    In what fucking universe would it not?

    Or was he one of those that thinks compilers didn't change since the 70s?



  • It was some n00b who had been taught that "you should always use ++i because i++ is expensive", which is reasonable advice when you're dealing with operator overloading on custom classes, but it's balls when applied to primitive types. To the guy's credit, he accepted that I was right, and I like to think that he learned something that day, and became a better programmer as a result...



  • --i is a nice thing to use when your coworkers also work with languages that lack -- but have unary - ...


  • BINNED

    @tar said:

    "you should always use ++i because i++ is expensive", which is reasonable advice when you're dealing with operator overloading on custom classes

    I never thought about that. I can see the logic in that though.

    Methinks I found a topic to read into a bit more when I'll have the time.



  • @Onyx said:

    Methinks I found a topic to read into a bit more when I'll have the time.

    Here's at least a basic discussion of the topic:


  • Discourse touched me in a no-no place

    I'm sure we've moved on, but...


    Having reached the end, I see we haven't. Central heating going full blast then...


    @accalia said:

    they're one fracking character shorter.... is that one character really that big of a deal?

    (In C) Idiomatically they're a lot shorter. If you're aware of the idiom that is. Possibly something to do with the increment operators using 50% of the characters that the other options use.

    Or the fact that it's used so heavily in existing code that forced used of an alternative just looks wrong. Like the i+=1 in the for loop in this post - that looks just as wrong as the following:

    for (var i=0; i < 42; i=i+1){
        foo(bar, i);
    }
    

    That said, if you're going to ban the increment operators for reasons, why not ban the op= operators too for the same reasons? Or different reasons. Or because it's the second blue moon this quarter or...

    Or simply go the whole hog and remove most of the syntactic sugar and create a function to do it (C):

    int* addone(int* i){
        *i^=*i&~-~*i|~*i&-~*i;
        return i;
    }
    
    for (int i=0; i<42; addone(&j){
        foo(bar, i);
    }
    

    And you can even chain it..

    addone(addone(addone(&j))); /* add three */
    

    So much better than those pesky ++i or i+=THREE statements littered all over the place...


    @boomzilla said:

    Yes! Spaces, people! Use them! Also, exclamation points!

    bool truthiness = !!some_non_bool;....

    But we've covered the '!! operator' on here before.


    @Buddy said:

    BTW, @pjh, do you know if upstream picked up any of the css refinements you added before?

    Which ones? There are so many...

    On a more serious note, theres always Discourse Default if you want to see what version of their stuff we have or try.discourse.org if you want to test what's current since we updated.


  • Discourse touched me in a no-no place

    @blakeyrat said:

    In which case, it's really saying nothing at all, because how can the read or write of a single word-sized value not be atomic? The CPU kind of requires it to be.

    Things can get really awful with multi-CPU architectures. (There's also the problem of unaligned reads, but “don't do that!” is the best response there.) The best thing to do with parallel programming is to stick to the Official API and get officious with locking things correctly, using the proper atomic access classes, that sort of thing. It avoids ever so much trouble.

    And avoid all that stuff unless you're sharing things between threads. You pay quite a bit of performance for it; a good parallel algorithm can make that performance hit back up and more, sometimes much more, but writing “a good parallel algorithm” that takes into account the foibles of modern hardware is non-trivial (i.e., PhD level work).



  • @accalia said:

    none, i @accalia'd. fixed.

    Actually, foo(bar), i; is perfectly good C/C++ syntax. It passes bar to foo(), discards the return value, and then returns the value of i as the value of the subexpression. If that's the whole statement, of course, it then discards the value that it retrieved from i, and a reasonable optimiser won't bother retrieving the value of ``i```.


  • ♿ (Parody)

    @Buddy said:

    Seriously, why isn't this shit just autotranslated into each person's preferred style on checkout or commit?

    The usage of clever stuff makes intent non-obvious even to people who know the person committing the code. Current technology stands no chance of figuring it out.

    So we're back to making stuff fool proof (until the better fool comes along, of course) and keeping dangerous but useful tools in the hands of skilled craftsmen (who are all at least part time fools).



  • Also, I can't be bothered actually reading all 220 posts here, so I don't know if anybody mentioned that i++ and i += 1 aren't interchangeable. The former returns the old value of i, and the latter returns the new value. Of course if you write these in separate statements all by themselves, it doesn't matter. And also, ++i performs better than i++ if i is an object with an overloaded operator, because preincrement doesn't need to make a copy of the object, and postincrement does.


  • ♿ (Parody)

    @aliceif said:

    --i is a nice thing to use when your coworkers also work with languages that lack -- but have unary - ...

    I find it's best when the language uses -- for line comments.


  • Discourse touched me in a no-no place

    @boomzilla said:

    I find it's best when the language uses -- for line comments.

    The only one I can think of, doesn't have pre/post operators.




  • FoxDev

    Wait… since when did Eggman have stilettos?



  • he obviously wanted an unknowable number of cakes for his men in heels party...


  • Discourse touched me in a no-no place

    @PJH said:

    The only one I can think of, doesn't have pre/post operators.

    VHDL uses -- for line comments.

    SGML uses -- for comments too, but that's block comments and not line comments. People tend to think it is <!--​/--> pairs, but that's because you've got to also shift into the SGML meta level <! before starting the comment. XML bastardized that around.



  • @PJH said:

    The only one I can think of, doesn't have pre/post operators.

    It’s probably for the best. Having -- for both comments and the decrement operator would create ambiguity in a lot of situations.

    (FYI -- is also used for comments in Lua and AppleScript)



  • Also, Lua.
    Which has a really strange block comment syntax in addition to that:

    -- normal comment
    
    --[[
    Yes. This.
    ]]
    

    Which the highlighter doesn't seem to know.


  • I survived the hour long Uno hand

    I wanted to give that a like, but there obviously are more than 24 cakes on that cart.


  • Java Dev

    The line you quoted me as saying was actually me quoting @ben_lubar



  • @boomzilla said:

    Yes! Spaces, people! Use them! Also, exclamation points!

    This post inspired me to write a bunch of words over at github https://github.com/dotnet/roslyn/issues/98#issuecomment-78474285

    @boomzilla said:

    The usage of clever stuff makes intent non-obvious even to people who know the person committing the code. Current technology stands no chance of figuring it out.

    So we're back to making stuff fool proof (until the better fool comes along, of course) and keeping dangerous but useful tools in the hands of skilled craftsmen (who are all at least part time fools).

    Yeah, I don't entirely understand people who always choose the most powerful language (whether that means the most low-level or the most high-level)—the way I see it, I've got all the power I need, right here, and what I really want from a language is discipline.

    Which is why it's great that I do most of my work in XAML; its heavy emphasis on ‘binding’ allows this whole post just to slide off into some bdsm innuendo without ever actually resolving...



  • @Mikael_Svahnberg said:

    int i=10;
    while (i --&gt; 0) { // i goes to 0
    // do something}
    

    DTFY, though why it has different behavior to backticks is beyond me.



  • @RaceProUK said:

    Wait… since when did Eggman have stilettos?

    Since when was he R. Crumb-esque 70s pop-art?



  • @TwelveBaud said:

    bitfield.

    Or std::bitset<> and its counterpart boost::dynamic_bitset...

    @Onyx said:

    @tar said:
    "you should always use ++i because i++ is expensive", which is reasonable advice when you're dealing with operator overloading on custom classes

    I never thought about that. I can see the logic in that though.

    Methinks I found a topic to read into a bit more when I'll have the time.


    TL;DR -- the postfix inc/dec operators can result in a copy ctor call that the prefix ones don't need, because of the semantic differences between the two. Most iterators are fairly cheap to copy, but still...

    @RaceProUK said:

    Wait… since when did Eggman have stilettos?

    Those are just regular heels, meh.



  • The Evil Ideas Thread is ↗ 🆒 ✅ that way



  • @Buddy said:

    Or maybe there needs to a lang where all code is stored on disk as ast

    You mean like Lisp? Ha Ha!

    But it's less-conspicuously true for most other languages, too.



  • That's funny. Now let me tell you about storing and distributing code in a .tar, where the directory structure corresponds to program structure. Nerdy Linux types just operate on the tree directly, bypassing braces and indenting and whatnot altogether. Low-power users get their IDE to translate it into their preferred format on the fly. Syntactic sugar becomes a personal preference. Only 144000 have been chosen.



  • @Buddy said:

    That's funny. Now let me tell you about storing and distributing code in a .tar, where the directory structure corresponds to program structure. Nerdy Linux types just operate on the tree directly, bypassing braces and indenting and whatnot altogether. Low-power users get their IDE to translate it into their preferred format on the fly. Syntactic sugar becomes a personal preference. Only 144000 have been chosen.

    Sounds like of like a Smalltalk image, but I'm not sure, never got familiar with Smalltalk.


  • BINNED

    @Buddy said:

    what I really want from a language is discipline.

    I have the perfect programming language for you.


  • Discourse touched me in a no-no place

    @Bort said:

    Sounds like of like a Smalltalk image, but I'm not sure, never got familiar with Smalltalk.

    I thought they used memory dumps, something like a core dump except with some code to relaunch from the state it was dumped in. (I've dealt with some languages which worked the same way, except they didn't on the platform I was on at the time and so using them was really annoying.)



  • I seem to remember at some point that ++i when compiled became a single INC operation rather than a MOV operation. Have compilers changed so much that they're not even optimizing correctly anymore? Or is INC not a part of the x86/AMD64 assembly language? I'm too lazy to actually google for the answer.


  • Java Dev

    ++i or i++ are both single assembly instructions, if i is an integer of the appropriate size. That operation may additionally be merged into another instruction - a[i++] is also a single instruction on certain architectures.

    However, C++ happened. i is assumed to be an object, the compiler is assumed to not know how the operators work (because overloading), and when it cannot be inlined i++ necessarily involves copying i.

    If i is known to be a primitive type, or i++ and ++i are operators whose definition is available so they can be inlined, then I don't see why i++ would be faster than ++i as a single statement.


  • Discourse touched me in a no-no place

    @Valarnin said:

    I seem to remember at some point that ++i when compiled became a single INC operation rather than a MOV operation.

    Yes, it was that way in the PDP-11, and also on the x86. Not many people worried about memory access atomicity with the PDP-11 as it was a long time ago, and the x86 operation isn't atomic by default. It's also not necessarily faster than a MOV/ADD/MOV sequence.



  • x86 INC doesn't set all flags, which can create a false dependency between instructions before it and instructions after. I realize that doesn't explain the MOVs, but remember he did use -O0. Anyway, as everyone knows, the fastest way to loop in x86 is while(i--) because LOOP is a single instruction that decrements the count register and jumps if not zero. (Works in other archs too, sub,jnz rather than add,cmp,jz)


  • Java Dev

    From your explanation, shouldn't that be while(--i)?



  • I don't have context for buddy's post but if it's like javascript using pre-decrement would skip the item at index 0.

    --i -> 1-1 = 0
    while(0) -> do not execute


  • Oh yeah, damn.

    Also I just remembered how useless LOOP is in most situations: stepping backwards through arrays is a cache-killer. The point is that optimizing x86 is a sucker's game, full of counterintuitive action-at-a-distance bullshit, where the assembly-level instructions have about as much relevance to the micro-ops that actually get executed as C code has to assembly.



  • @accalia said:

    so i forbid ++ and -- in projects i have control over. it's not that big a deal and it means you can't pull that shite on me.

    @accalia, you're like twelve. There is no way you possibly have enough life experience to be setting such totalitarian rules. I'm not trying to judge you too harshly here, I'm also young, and I know how easy it is to think you know what's right; anybody with half an ounce of sense has had people telling them how smart they are since at least second grade. But if I pulled some shit like this, I would want people to call me out on it.

    It is a big deal. You are in a position of power, and issuing blanket bans on perfectly valid ways for people to express themselves really seems like an abuse of that power.

    If you want to enforce best practices, good for you, but the good-faith way to go about that is to conduct a survey, with an open mind, of widely-accepted best practices and enforce those, not to start with your own personal quirks and preconceptions and then scramble to provide justification for them. Don't be a Chomsky.

    And yes, I realize that programming best practices are like waves in the ocean. The trick is to find a nice clean one to hop on, and be ready to dismount safely before it breaks.


Log in to reply