Author Topic: postfix and prefix incrementing  (Read 13972 times)

0 Members and 1 Guest are viewing this topic.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
postfix and prefix incrementing
« on: February 02, 2006, 03:48:24 AM »
Bronson is autistic and has cerebral palsy. Consequently he has a fascination with numbers and likes an ordered existence.

Today I was trying to explain the logic of a for loop.

Things were fine till we got to postfix and prefix incrementing.

I ended up using this to help explain the difference.

So ... just in case anyone else has wondered ...
Code: [Select]
using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication_PostAndPrefix
{
    class Program
    {
        static void Main(string[] args)
        {
            int xValue, n, p;
            //
            Console.WriteLine("Postfix++ and ++Prefix Examples for Shorty");

            xValue = 100;
            Console.WriteLine("xValue is {0}" , xValue.ToString());
            Console.WriteLine("----------------");

            xValue = 100;
            Console.WriteLine("xValue is {0}", xValue.ToString());
            Console.WriteLine("Postfix++ uses the current value, then adds 1 and saves.");
            Console.WriteLine("xValue++ is {0}", (xValue++).ToString());
            Console.WriteLine("xValue is {0}", xValue.ToString());
            Console.WriteLine("----------------");

            xValue = 100;
            Console.WriteLine("xValue is {0}", xValue.ToString());
            Console.WriteLine("++Prefix adds 1 and saves, then uses the new value.");
            Console.WriteLine("++xValue is {0}", (++xValue).ToString());
            Console.WriteLine("xValue is {0}", xValue.ToString());
            Console.WriteLine("----------------");

            n = 1;
            p = ++n;
            Console.WriteLine("n = 1;  p = ++n;");
            Console.WriteLine("n is {0}, p is {1}", n.ToString(), p.ToString() );
            Console.WriteLine("----------------");

            n = 1;
            p = n++;
            Console.WriteLine("n = 1;  p = n++;");
            Console.WriteLine("n is {0}, p is {1}", n.ToString(), p.ToString());

        }
    }
}

/*

Postfix++ and ++Prefix Examples for Shorty
xValue is 100
----------------
xValue is 100
Postfix++ uses the current value, then adds 1 and saves.
xValue++ is 100
xValue is 101
----------------
xValue is 100
++Prefix adds 1 and saves, then uses the new value.
++xValue is 101
xValue is 101
----------------
n = 1;  p = ++n;
n is 2, p is 2
----------------
n = 1;  p = n++;
n is 2, p is 1
Press any key to continue . . .

*/

added : n and p variable calcs added for clarification
« Last Edit: February 02, 2006, 09:49:12 AM by Kerry Brown »
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: postfix and prefix incrementing
« Reply #1 on: February 02, 2006, 04:16:31 AM »
Probably should post this too 'cause it's sort-of relevent .. The For Loop stuff ..
Code: [Select]
using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication_ForLoops
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] nameArray = { "zero", "one", "two", "three", "four" };
            Console.WriteLine("nameArray.Length : {0}", nameArray.Length.ToString());

            for (int index = 0; index < nameArray.Length; ++index) {
                Console.Write("Index {0} : ", index.ToString());
                Console.WriteLine(nameArray[index]);
            }
        }
    }
}
/*
nameArray.Length : 5
Index 0 : zero
Index 1 : one
Index 2 : two
Index 3 : three
Index 4 : four
Press any key to continue . . .
*/

modified :  incrementer index++)  changed to ++index)
« Last Edit: February 02, 2006, 10:11:14 AM by Kerry Brown »
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

HD

  • Guest
Re: postfix and prefix incrementing
« Reply #2 on: February 02, 2006, 08:30:22 AM »
Since I have never been a fulltime C, C++, or C# programmer I have always had to go back and "hit" the book / on-line documentation everytime I wanted to use the increment / decrement operators. It use to frustrate me that I couldn't remember something so simple.

When I saw this post I had to respond because one of my C# reference books (C# For Dummies) included the history of these operators. I stashed this history away because I remember hearing a college instructor saying, "Industry won't consider you to be a true C programmer unless you use pointers and the increment / decrement operators."

After reading the history of the increment / decrement operators, if Bronson feels more comfortable using the longer but more intuitive notation -- he should go for it.


History of the increment / decrement operators:

Why have an increment operator, and why two of them?
The reason for the increment operator lies in the obscure fact that the PDP-8 computer of the 1970’s had an increment instruction. This would be of little interest today were it not for the fact that the C language, the original precursor to C#, was originally written for the PDP-8. Because that machine had an increment instruction, n++ generated fewer machine instructions than n = n + 1. As slow as those machines were, saving a few machine instructions was a big deal.
 
Today, compilers are smarter and there’s no difference in the execution time for n++ and n = n + 1 so the need for the increment operator has gone away. However, programmers are creatures of habit, and the operator remains to this day. You almost never a C++ programmer increment a value using the longer but more intuitive n = n + 1. Instead, you see the increment operator.
 
Further, when standing by itself (that is, not part of a larger expression), the post-increment operator almost always appears instead of the pre-increment. There’s no reason other than habit and the fact that it looks cooler.
 
C# actually has has two increment operators: ++n and n++ The first one, ++n, is called the preincrement operator, while n++ is the postincrement, The difference is subtle but important.
 
int n;
n = 1;
int o = ++n; // the value of o is 2. ++n is the value of n after being incremented.
 
n = 1;
int m = n++ the // the value of m is 1. n++ is the value of n before it is incremented.
 
Either way, the resulting value value of n is 2.
 
Equivalent decrement operators - that is, n-- and --n - exist to replace n = n -1. These work in exactly the same way as the increment operators.

Chuck Gabriel

  • Guest
Re: postfix and prefix incrementing
« Reply #3 on: February 02, 2006, 08:50:52 AM »
It is worth noting that unless you intend to use the return value of the increment AND it needs to reflect the pre-increment value, you should use the prefix increment because it is more efficient.

Postfix increment has to store the current value in a temporary location, then increment the original, then return the temporary.  Prefix increment just increments the value in place and returns it.

Of course, the difference is trivial for integral types, but it can become an issue when dealing with objects (think iterators) that have prefix and postfix increment operators, or when dealing with long loops, so it is a good habit to get into.

Having said all that, I think the real issue here is "who is Bronson?"

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: postfix and prefix incrementing
« Reply #4 on: February 02, 2006, 09:07:28 AM »
Interesting History Nick.

Quote
It is worth noting that unless you intend to use the return value of the increment AND it needs to reflect the pre-increment value, you should use the prefix increment because it is more efficient.

Good point chuck, the joy is in the details, heh.

Bronson is my nephew, who's been living with us most of his life. Gentle soul. Not rainmanish exactly, but has an intimate knowledge of bus timetables.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Chuck Gabriel

  • Guest
Re: postfix and prefix incrementing
« Reply #5 on: February 02, 2006, 09:15:12 AM »
Thanks for sharing that.

You're a good man Kerry Brown.

Draftek

  • Guest
Re: postfix and prefix incrementing
« Reply #6 on: February 02, 2006, 09:32:29 AM »
Ditto,

Thanks for sharing the example and the info.

David Hall

  • Automatic Duh Generator
  • King Gator
  • Posts: 4075
Re: postfix and prefix incrementing
« Reply #7 on: February 02, 2006, 10:10:28 AM »
Quote
It is worth noting that unless you intend to use the return value of the increment AND it needs to reflect the pre-increment value, you should use the prefix increment because it is more efficient.
I thought I read the exact opposite.  I will have to look that up if I can find it
Everyone has a photographic memory, Some just don't have film.
They say money can't buy happiness, but it can buy Bacon and that's a close second.
Sometimes the question is more important than the answer. (Thanks Kerry for reminding me)

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: postfix and prefix incrementing
« Reply #8 on: February 02, 2006, 10:18:57 AM »
Hi David,

The ++index is not evaluated untill the for loop runs once.
On the second pass the index is incremented and then the conditional test is performed.

I had to step it through the debugger to make sure too :-)
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Chuck Gabriel

  • Guest

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: postfix and prefix incrementing
« Reply #10 on: February 02, 2006, 10:50:34 AM »
Usefull site Chuck .. linked as a keeper.
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: postfix and prefix incrementing
« Reply #11 on: February 05, 2006, 04:26:32 AM »
I didn't have enough time to finish my investigation of this, so I'll just pop it up and see if anyone wants to make a qualified < or otherwise :-) > comment.

This code ;
Code: [Select]
        public int preInctement()
        {
            int result = 1;
            int tempVal = 1;
            result = ++tempVal;

            return result;
        }
        public int postInctement()
        {
            int result = 1;
            int tempVal = 1;
            result = tempVal++;

            return result;
        }
Produces this IL <Intermediate Language> code when run through the ILDASM Disassembler.
Code: [Select]
.method public hidebysig instance int32  preInctement() cil managed
{
  // Code size       17 (0x11)
  .maxstack  2
  .locals init ([0] int32 result,
           [1] int32 tempVal,
           [2] int32 CS$1$0000)
  .language '{3F5162F8-07C6-11D3-9053-00C04FA302A1}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}'
// Source File 'L:\VisualStudio2005\Projects\2229\ConsoleApplication-PostAndPrefix\ConsoleApplication-PostAndPrefix\Program.cs'
//000045:         {
  IL_0000:  nop
//000046:             int result = 1;
  IL_0001:  ldc.i4.1
  IL_0002:  stloc.0
//000047:             int tempVal = 1;
  IL_0003:  ldc.i4.1
  IL_0004:  stloc.1
//000048:             result = ++tempVal;
  IL_0005:  ldloc.1
  IL_0006:  ldc.i4.1
  IL_0007:  add
  IL_0008:  dup
  IL_0009:  stloc.1
  IL_000a:  stloc.0
//000049:
//000050:             return result;
  IL_000b:  ldloc.0
  IL_000c:  stloc.2
  IL_000d:  br.s       IL_000f
//000051:         }
  IL_000f:  ldloc.2
  IL_0010:  ret
} // end of method Program::preInctement


.method public hidebysig instance int32  postInctement() cil managed
{
  // Code size       17 (0x11)
  .maxstack  3
  .locals init ([0] int32 result,
           [1] int32 tempVal,
           [2] int32 CS$1$0000)
  .language '{3F5162F8-07C6-11D3-9053-00C04FA302A1}', '{994B45C4-E6E9-11D2-903F-00C04FA302A1}', '{5A869D0B-6611-11D3-BD2A-0000F80849BD}'
// Source File 'L:\VisualStudio2005\Projects\2229\ConsoleApplication-PostAndPrefix\ConsoleApplication-PostAndPrefix\Program.cs'
//000053:         {
  IL_0000:  nop
//000054:             int result = 1;
  IL_0001:  ldc.i4.1
  IL_0002:  stloc.0
//000055:             int tempVal = 1;
  IL_0003:  ldc.i4.1
  IL_0004:  stloc.1
//000056:             result = tempVal++;
  IL_0005:  ldloc.1
  IL_0006:  dup
  IL_0007:  ldc.i4.1
  IL_0008:  add
  IL_0009:  stloc.1
  IL_000a:  stloc.0
//000057:
//000058:             return result;
  IL_000b:  ldloc.0
  IL_000c:  stloc.2
  IL_000d:  br.s       IL_000f
//000059:         }
  IL_000f:  ldloc.2
  IL_0010:  ret
} // end of method Program::postInctement




There seems to be the same number of instructions, BUT notice the .maxstack allowance ;
... the postInctement() method expects to use 3 stack slots while preInctement() expects to use 2.

These are indicated in the code by ldc.i4.1 which is used to push value<s> onto the stack, ie save them for later use
The anomolie is that BOTH methods actually use  ldc.i4.1 3 times, irrespective of the .maxstack allowance.

weird, heh ..

Has anyone passing by done any study regarding the resulting IL code.

Have a good week ...

« Last Edit: February 05, 2006, 04:36:37 AM by Kerry Brown »
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: postfix and prefix incrementing
« Reply #12 on: February 05, 2006, 07:54:19 PM »
For those interested in deciphering the above MSIL (Microsoft Intermediate Language), here is a brief intro and tutorial by Kenny Kerr -> http://weblogs.asp.net/kennykerr/category/7140.aspx
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: postfix and prefix incrementing
« Reply #13 on: February 05, 2006, 08:48:13 PM »

There seems to be the same number of instructions, BUT notice the .maxstack allowance ;
... the postInctement() method expects to use 3 stack slots while preInctement() expects to use 2.

These are indicated in the code by ldc.i4.1 which is used to push value<s> onto the stack, ie save them for later use
The anomolie is that BOTH methods actually use ldc.i4.1 3 times, irrespective of the .maxstack allowance.

weird, heh ..

Has anyone passing by done any study regarding the resulting IL code.

Have a good week ...



After a quick study the second function pushes 1 item then 'dup' s (duplicates) the top item on the stack(another push), then pushes another item on the stack giving you 3 items before the add instruction whereas the first section only has 2 pushes before the add instruction.

++i code with comments
Code: [Select]
  IL_0005:  ldloc.1      //1st push
  IL_0006:  ldc.i4.1     //2nd push
  IL_0007:  add          //pop 2, add and push result, total stack = 1
  IL_0008:  dup         //pop the top (stack=0)and duplicate, push original and copy = 2 pushes, stack = 2
  IL_0009:  stloc.1      //pop 1
  IL_000a:  stloc.0      //pop 2, stack = 0( balanced)

i++ code with comments
Code: [Select]
  IL_0005:  ldloc.1     //push 1
  IL_0006:  dup         //pop 1 and push 2, stack = 2
  IL_0007:  ldc.i4.1    //push another, stack now = 3
  IL_0008:  add         //pop 2, add and push result, stack = 2
  IL_0009:  stloc.1     //pop 1
  IL_000a:  stloc.0     //pop 2, stack = 0 (balanced)
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: postfix and prefix incrementing
« Reply #14 on: February 05, 2006, 08:57:16 PM »
From a further look this morning, I think that IS the case Mick.

I was originally considering instructions only, not net maximum usage allocation.

Thanks for having a play ..
« Last Edit: February 05, 2006, 09:13:34 PM by Kerry Brown »
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Chuck Gabriel

  • Guest
Re: postfix and prefix incrementing
« Reply #15 on: February 05, 2006, 09:39:11 PM »
Here is something to think about (please ignore the fact that this appears to contradict my previous statements :D).  MSVC++6 "compiled" the following C source code:

Code: [Select]
int main() {
int i = 0;
int retVal;
retVal = ++i;
retVal = i++;
return 0;
}

into this assembler code with all optimizations turned off.  The source code is shown inline prior to its assembly equivalent.

Code: [Select]
TITLE C:\My Documents\Programming\Projects\C++\Experiments\increment\main.cpp
.386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT ENDS
_DATA SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA ENDS
CONST SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST ENDS
_BSS SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS ENDS
_TLS SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS ENDS
FLAT GROUP _DATA, CONST, _BSS
ASSUME CS: FLAT, DS: FLAT, SS: FLAT
endif
PUBLIC _main
_TEXT SEGMENT
_i$ = -4
_retVal$ = -8
_main PROC NEAR

; 1    : int main() {

push ebp
mov ebp, esp
sub esp, 8

; 2    : int i = 0;

mov DWORD PTR _i$[ebp], 0

; 3    : int retVal;
; 4    : retVal = ++i;

mov eax, DWORD PTR _i$[ebp]
add eax, 1
mov DWORD PTR _i$[ebp], eax
mov ecx, DWORD PTR _i$[ebp]
mov DWORD PTR _retVal$[ebp], ecx

; 5    : retVal = i++;

mov edx, DWORD PTR _i$[ebp]
mov DWORD PTR _retVal$[ebp], edx
mov eax, DWORD PTR _i$[ebp]
add eax, 1
mov DWORD PTR _i$[ebp], eax

; 6    : return 0;

xor eax, eax

; 7    : }

mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: postfix and prefix incrementing
« Reply #16 on: February 05, 2006, 09:56:53 PM »

BabelFish spat the dummy on the translation Chuck :-) ...  help ! ! !
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

Chuck Gabriel

  • Guest
Re: postfix and prefix incrementing
« Reply #17 on: February 05, 2006, 10:00:56 PM »
The only part worth understanding is the fact that i++ and ++i both took four mov instructions and one add instruction.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: postfix and prefix incrementing
« Reply #18 on: February 05, 2006, 10:07:48 PM »
ta !   :lol:
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

MickD

  • King Gator
  • Posts: 3636
  • (x-in)->[process]->(y-out) ... simples!
Re: postfix and prefix incrementing
« Reply #19 on: February 05, 2006, 10:16:23 PM »
Bablefish modified code :D
Code: [Select]
_i$ = -4            ;4 bytes into the stack = start address for 1 double word variable
_retVal$ = -8       ;8 bytes into the stack = start address for another dword variable
_main PROC NEAR     ;address of main procedure


; 1    : int main() {

push ebp                          ;preserve the ebp register
mov ebp, esp                      ;move the stack pointer into ebp
sub esp, 8                        ;subtract 8 bytes from the stack pointer
                                  ;(move 2 dwords (64bits) into the stack)
                                  ;to allow for our variables _i$ and _retVal$

; 2    : int i = 0;

mov DWORD PTR _i$[ebp], 0         ;move 0 into address in ebp (_i$,this is the start of the stack!)

; 3    : int retVal;         
; 4    : retVal = ++i;

mov eax, DWORD PTR _i$[ebp]          ;move what's in _i$ into eax
add eax, 1                           ;add 1 to it (increment) eax, result in eax (0+1=1)
mov DWORD PTR _i$[ebp], eax          ;move result back into _i$(ebp)
mov ecx, DWORD PTR _i$[ebp]          ;move _i$ into ecx (ecx = 1)
mov DWORD PTR _retVal$[ebp], ecx     ;move ecx into _retVal

; 5    : retVal = i++;

mov edx, DWORD PTR _i$[ebp]         ;move _i$ into edx
mov DWORD PTR _retVal$[ebp], edx    ;move edx into _retVal$
mov eax, DWORD PTR _i$[ebp]         ;move _i$ into eax
add eax, 1                          ;add 1 to eax, eax = 2, we haven't emptied eax!
mov DWORD PTR _i$[ebp], eax         ;move result back into _i$

; 6    : return 0;

xor eax, eax        ;set all bits to zero in eax to clear it.

; 7    : }

mov esp, ebp        ;move the top of the stack back into stack pointer
                    ;register to balance the stack (like poping twice)
pop ebp             ;restore the ebp register
ret 0
_main ENDP
_TEXT ENDS
END   

As Chuck said, it's the same instruction count but notice we didn't do any 'pop'ing, we just get the stack pointer (address of stack for arguments sake) and index into and out of the stack using the addresses of the variable calculated by the size of the variables and the stack address.
I'd guess that MSIL does it this way as IL is a 'pseudo' assembler, ie it has to be parsed and compiled into 'proper' assembler/machine instructions before use. Therefore it can create it's own 'compile time' stacks etc.
"Programming is really just the mundane aspect of expressing a solution to a problem."
- John Carmack

"Short cuts make long delays,' argued Pippin.”
- J.R.R. Tolkien

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: postfix and prefix incrementing
« Reply #20 on: February 05, 2006, 10:27:17 PM »
ta !   :lol:

..and ditto to Mick .

If I recall, Jon Skeet or Jess Liberty suggested using postfix by default, just for consistancy, unless  prefix was Explicitly needed.

The options could always be benchmarked I s'pose, but I can't see that it would be a really critical issue considering the IL is 'compiled' after Jitting anyway.

Interesting discussion ..
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.