Just like guestgulkan, I have not met this order of operands before.
I think the compiler utilizes the address arithmetic of the CPU in order to perform integral calculations in fewer steps. Actually the method is used/abused quite frequently. I'm going to indulge in some reverse engineering of the code, as it is impressive what the compilers sometimes do under the hood.
1 2
|
80483c4: 55 push %ebp
80483c5: 89 e5 mov %esp,%ebp
|
According to google, the base pointer is anchored so that it can be used for referring to the local variables with constant offsets. Here it seems to be used only to guarantee that the stack pointer is properly restored on return from the function.
|
80483c7: 83 e4 f0 and $0xfffffff0,%esp
|
Aligns the new stack frame on the 16 byte boundary. I think mainly to improve cache performance, but ISO C++ may have some alignment requirements of its own.
|
80483ca: 83 ec 20 sub $0x20,%esp
|
Simultaneously allocates space for the new stack frame and for the parameters to called functions, e.g.
printf, in one go. This way no time is wasted to allocate stack space on a per-need basis.
1 2
|
80483cd: c7 44 24 1c 02 00 00 movl $0x2,0x1c(%esp)
80483d4: 00
|
Moves the value 2 into one of your variables. If the compilation is code for code, as I believe it is, then this is the initialization for
i. 0 is probably for alignment, or to help the CPU somehow. I am not sure.
1 2
|
80483d5: 8b 44 24 1c mov 0x1c(%esp),%eax
80483d9: 89 44 24 18 mov %eax,0x18(%esp)
|
Copies the value of
i into
j. (Actually copies the result from the assignment of
i into
j.) That should be slower to directly initializing with 2, but you probably made a debug build and the assembly must map to the source code perfectly.
1 2
|
80483dd: 8b 44 24 18 mov 0x18(%esp),%eax
80483e1: 8b 54 24 1c mov 0x1c(%esp),%edx
|
Loads
i and
j into registers.
|
80483e5: 8d 04 02 lea (%edx,%eax,1),%eax
|
Performs address arithmetic, computing the address location i + j + 1:
i assumes the role of the base address,
j assumes the role of the index, added (with no scaling) to the base address, and 1 is constant displacement (offset), which is added to the now indexed address. This is like computing i[j].*1 in some pseudo C++ notation.
|
80483e8: 89 44 24 14 mov %eax,0x14(%esp)
|
Now, the computed 'address' is stored into s. Something like cheating.
1 2
|
80483ec: 83 44 24 1c 01 addl $0x1,0x1c(%esp)
80483f1: 83 44 24 18 01 addl $0x1,0x18(%esp)
|
The side effects from the postfix and prefix increments. It must be a debug build indeed. (The value is directly 3.)
|
80483f6: b8 e4 84 04 08 mov $0x80484e4,%eax
|
Loads the address of your
printf format string.
1 2 3 4
|
80483fb: 8b 54 24 1c mov 0x1c(%esp),%edx
80483ff: 89 54 24 08 mov %edx,0x8(%esp)
8048403: 8b 54 24 14 mov 0x14(%esp),%edx
8048407: 89 54 24 04 mov %edx,0x4(%esp)
|
Copies the values of the substitution arguments to
printf (
i and
s). A lot of stack space is still hanging unused. Probably for variables in the function body after returning from the call, or for the arguments of the following calls.
1 2
|
804840b: 89 04 24 mov %eax,(%esp)
804840e: e8 e1 fe ff ff call 80482f4 <printf@plt>
|
Shoves the format string on the stack and calls.