I++ vs i+=1
-
>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
-
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 arenew
anddelete
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...
-
-
-
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.
-
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?
-
-
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_startprocBB#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_startprocBB#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++)
andfor(int i = 0; i < n; ++i)
produce identical code, which allowed myi++
to stand code review.
-
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
?
-
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-
...
-
"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.
-
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:
-
I'm sure we've moved on, but...
Having reached the end, I see we haven't. Central heating going full blast then...
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
ori+=THREE
statements littered all over the place...
Yes! Spaces, people! Use them! Also, exclamation points!
bool truthiness = !!some_non_bool;
....But we've covered the '
!!
operator' on here before.
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.
-
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).
-
none, i @accalia'd. fixed.
Actually,foo(bar), i;
is perfectly good C/C++ syntax. It passesbar
tofoo()
, discards the return value, and then returns the value ofi
as the value of the subexpression. If that's the whole statement, of course, it then discards the value that it retrieved fromi
, and a reasonable optimiser won't bother retrieving the value of ``i```.
-
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++
andi += 1
aren't interchangeable. The former returns the old value ofi
, 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 thani++
ifi
is an object with an overloaded operator, because preincrement doesn't need to make a copy of the object, and postincrement does.
-
--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.
-
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.
-
-
Wait… since when did Eggman have stilettos?
-
he obviously wanted an unknowable number of cakes for his men in heels party...
-
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.
-
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 wanted to give that a like, but there obviously are more than 24 cakes on that cart.
-
The line you quoted me as saying was actually me quoting @ben_lubar
-
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
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...
-
int i=10; while (i --> 0) { // i goes to 0 // do something}
DTFY, though why it has different behavior to backticks is beyond me.
-
Wait… since when did Eggman have stilettos?
Since when was he R. Crumb-esque 70s pop-art?
-
bitfield.
Or
std::bitset<>
and its counterpartboost::dynamic_bitset
...@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...Wait… since when did Eggman have stilettos?
Those are just regular heels, meh.
-
The Evil Ideas Thread is that way
-
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.
-
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.
-
what I really want from a language is discipline.
I have the perfect programming language for you.
-
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.
-
++i
ori++
are both single assembly instructions, ifi
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 inlinedi++
necessarily involves copyingi
.If
i
is known to be a primitive type, ori++
and++i
are operators whose definition is available so they can be inlined, then I don't see whyi++
would be faster than++i
as a single statement.
-
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)
-
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.
-
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.