In K&R Ch 1:
The statement
++ncpresents a new operator,++, which means increment by one. You could instead writenc = nc + 1, but++ncis more concise and often more efficient.
When would pre-increment be more efficient than the alternative? For most things, at least, the assembly for both is the add (edit: or inc) instruction. When do they differ?
5 Answers
That text is long out dated. It might have been true in the 70's that compilers would produce more efficient output for ++n, but not any more. All modern compilers will produce identical code.
For most things, at least, the assembly for both is the add instruction.
That's not quite true: there is often a separate "increment by one" instruction. However, that's irrelevant since any half-decent compiler will produce identical machine code for ++nc and nc = nc + 1.
In other words, there is no performance difference. There may have been when the book was written and compilers were not very good, but there isn't anymore.
I don't know for sure, I'm just thinking out aloud (maybe I shouldn't): Perhaps in K&R's time, ++nc was compiled into something more efficient than nc = nc + 1 (e.g., an increment instruction, rather than an addition). Nowadays, however, compilers probably optimise this automagically.
This is what I could see with gcc -S <filename>. I'll let you derive what you want!
>
> cat 1.c
#include <stdio.h>
int main()
{
int i=0;
++i;
return 0;
}
>
> cat 2.c
#include <stdio.h>
int main()
{
int i=0;
i++;
return 0;
}
>
> cat 3.c
#include <stdio.h>
int main(void)
{
int i=0;
i = i + 1;
return 0;
}
>
> gcc -S 1.c 2.c 3.c
>
>
> diff 1.s 2.s
1c1
< .file "1.c"
---
> .file "2.c"
>
> diff 2.s 3.s
1c1
< .file "2.c"
---
> .file "3.c"
>
> diff 3.s 1.s
1c1
< .file "3.c"
---
> .file "1.c"
>
>
The below is the content of the .s file for 1.c and the instructions are identical when compared with 2.s and 3.s!
> cat 1.s
.file "1.c"
.text
.globl main
.type main, @function
main:
.LFB2:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
movl $0, -4(%rbp)
addl $1, -4(%rbp)
movl $0, %eax
leave
ret
.LFE2:
.size main, .-main
.section .eh_frame,"a",@progbits
.Lframe1:
.long .LECIE1-.LSCIE1
.LSCIE1:
.long 0x0
.byte 0x1
.string "zR"
.uleb128 0x1
.sleb128 -8
.byte 0x10
.uleb128 0x1
.byte 0x3
.byte 0xc
.uleb128 0x7
.uleb128 0x8
.byte 0x90
.uleb128 0x1
.align 8
.LECIE1:
.LSFDE1:
.long .LEFDE1-.LASFDE1
.LASFDE1:
.long .LASFDE1-.Lframe1
.long .LFB2
.long .LFE2-.LFB2
.uleb128 0x0
.byte 0x4
.long .LCFI0-.LFB2
.byte 0xe
.uleb128 0x10
.byte 0x86
.uleb128 0x2
.byte 0x4
.long .LCFI1-.LCFI0
.byte 0xd
.uleb128 0x6
.align 8
.LEFDE1:
.ident "GCC: (GNU) 4.1.2 20080704 (Red Hat 4.1.2-48)"
.section .note.GNU-stack,"",@progbits
>
For "normal" variables there should be no difference as other answers suggest. Only if nc is voloatile qualified the result could be different. For such a variable the +1 form must first evaluate the expression nc that is load nc and then perform the addition. For the ++ form the compiler still could take shortcuts and increment the variable in place.