http://qs1969.pair.com?node_id=1118712

sam_bakki has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks

I have the following code, IMHO it should print "T1" but to my surprise, It print "T2". Then I compile the "C" version of the same code in gcc and executed, It also printed "T2" , Then the C version, I have compiled in Visual Studio, It prints "T1" "T2".

I am confused now. Can some one please throw light?

use strict; my $a = 10; #IMHO: $a-- should finish the current operation and then -1 if ($a == $a--) { print "\n T1"; } $a=10; #IMHO: --$a should -1 first and continute with current operation , i.e + if compare if ($a == --$a) { print "\n T2"; } #ENV: #Active Perl 5.20, Windows 7, x64 #Output # T2

Thanks & Regards,
Bakkiaraj M
My Perl Gtk2 technology demo project - http://code.google.com/p/saaral-soft-search-spider/ , contributions are welcome.

Replies are listed 'Best First'.
Re: Help me to understand increment / decrement operator behavior in perl
by andal (Hermit) on Mar 04, 2015 at 08:24 UTC

    These increments/decrements cause quite a confusion :)

    Actually one should remember, that postfix operation changes variable after the value is taken. It is not specified when exactly this "after" happens. One can be sure only, that when the value is taken next time, then it shall be changed value. Now, if you are referencing the same variable within single expression, then everything depends on time of access relative to postfix operation. If it is accessed before, then result shall be value before change. If it is accessed after, then it shall be changed value.

    The problem is, most of the time it is not defined when the operation shall access variables. It is left up to the language implementation. So, perl operation $a == $a-- is not required to access content of the left operand before right one, or vice versa. Actually, I believe in this case opcode for $a simply marks address of the variable where value is stored, while opcode for $a-- first creates temporary variable referencing old content and then decrements content of the variable. So, the opcode for ==, which is executed after decrement, gets values from $a (which is already decremented) and then from temporary variable created by post-decrement. Of course the values are different.

    So, general rule is: don't use variable with increments/decrements multiple times within single expression. You are only running into trouble, since in different implementations such expression may run differently. In this particular case, future implementations of perl could obtain values instead of marking address and then behavior would change.

Re: Help me to understand increment / decrement operator behavior in perl
by Anonymous Monk on Mar 04, 2015 at 06:27 UTC
    The C standard says:
    Between the previous and next sequence point a scalar object shall have its stored value modified at most once by the evaluation of an expression. Furthermore, the prior value shall be accessed only to determine the value to be stored.
    The == operator doesn't introduce a sequence point. You're trying to access the prior value of the variable to 1) determine the value to be stored 2) determine the value to be compared with whatever increment operator yields. The C standard calls that 'undefined behavior', which means anything can happen.

    perlop says:

    Note that just as in C, Perl doesn't define when the variable is incremented or decremented. You just know it will be done sometime before or after the value is returned.
Re: Help me to understand increment / decrement operator behavior in perl
by roboticus (Chancellor) on Mar 04, 2015 at 11:33 UTC

    sam bakki:

    You've already received the answer, but here's a little history:

    Certain things were left undefined to allow flexibility in implementation as well as allow the compiler to generate fast code. The various CPU architectures all had various quirks, so the best way to code some statements turns out to be very different on the different architectures. As an example, some CPUs didn't have automatic stack handling, but had addressing modes with preincrement and postdecrement. So the statements on the left would compile into faster code that the code on the right:

    FasterSlower
    --g;g--;
    g++;++g;

    But on another CPU, the opposite could occur, and on yet another, neither would be faster or slower. When you combine some operations into a single expression, then if the behaviour were precisely defined, you'd penalize one CPU architecture, forcing it to generate worse code in many cases. There are (IIRC) similar issues with endianness, such as:

    union foo { long a; char b; }; int main(int, char**) { foo temp; temp.a = 0x30313233; // ASCII values of '0', '1', '2', '3' putchar(temp.b); );

    One some architectures, when you run this code, you'll see the computer print a 0, while on others you'd get 3 because of the order in which the CPU would lay out bytes.

    There are other issues, too, where the sizes of data types would vary by architecture (the Honeywell 6000 used 9 bits for char, 36 bits for int and 72 bits for double), character set (IBM would use EBCDIC), how structures were laid out in memory (some architectures would have a bus fault if you tried to access a word at an odd byte boundary (they wouldn't do two memory accesses for a fetch).

    So when they were ironing out the standards, they punted on some decisions to allow code to be generated in the most efficient manner on the various architectures. It's one of the reasons that portability can be tricky in C. On the bright side, it's one of the reasons that C became such a widely-used language, and displaced assembly language programming--it's a bit higher level, but still lets you tune your code to be performant. When you become proficient in C, you'll get used to the oddities (especially when you write embedded code on various microprocessors).

    When perl was created, it inherited some oddities from C.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

Re: Help me to understand increment / decrement operator behavior in perl
by hdb (Monsignor) on Mar 04, 2015 at 06:21 UTC

    This probably falls in a similar category as discussed in 1118529. After all, you compare $a to $a while changing it at the same time.

Re: Help me to understand increment / decrement operator behavior in perl
by vinoth.ree (Monsignor) on Mar 04, 2015 at 06:07 UTC
    Hi,

    I have tried your code with two different variables as below, Is works as expected.

    use strict; use warnings; my $a = 10; my $b = 10; #IMHO: $a-- should finish the current operation and then -1 if ($a == $b--) { print "\n T1"; } $b=10; #IMHO: --$a should -1 first and continute with current operation , i.e + if compare if ($a == --$b) { print "\n T2"; } #ENV: #Active Perl 5.20, Windows 7, x64 #Output # T2

    Unary

    The auto-decrement (--), and auto-increment (++) operators are unary operators. They alter the scalar variable they operate on by one logical unit. On numbers, they add or subtract one.Operators that come in post- and pre- varieties can be used two ways. The first way returns the value of the variable before it was altered, and the second way returns the value of the variable after it was altered.

    my $foo = 1; # post decrement (printed and then decremented to 0) print $foo--; # prints 1 print $foo; # prints 0
    my $foo = 1; # pre-decrement (decremented to 0 then printed) print --$foo; # prints 0 print $foo; # prints 0


    All is well. I learn by answering your questions...
Re: Help me to understand increment / decrement operator behavior in perl
by ikegami (Patriarch) on Mar 04, 2015 at 16:41 UTC

    $a places scalar $a on the stack (the variable $a, not its value 10).

    $a-- places a scalar containing the original value of $a on the stack, so $a == $a-- compares $a with the original value of $a. They will never be equal.

    --$a places scalar $a on the stack, so $a == --$a compares $a and $a. They will always be equal.


    C behaves differently since it places values on the stack or in the registers. You can replicate the behaviour using pointers in C or references in C++.

    #include <stdio.h> #include <stdlib.h> int& postdec(int& a) { int* rv_ptr = (int*)malloc(sizeof(int)); *rv_ptr = a; --a; return *rv_ptr; } int& predec(int& a) { --a; return a; } bool& eq(int& a, int& b) { bool* rv_ptr = (bool*)malloc(sizeof(bool)); *rv_ptr = a == b; return *rv_ptr; } int main() { int a = 10; if (eq(a, postdec(a))) { printf("T1\n"); } a = 10; if (eq(a, predec(a))) { printf("T2\n"); } }

    Update: Removed overspecification identified by BrowserUk.

      C behaves differently since it places values on the stack.

      Um. Actually, C doesn't place anything on the stack. It loads the values into registers.

      This is the MS compiler:

      ; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00 +.21022.08 include listing.inc INCLUDELIB LIBCMT INCLUDELIB OLDNAMES _DATA SEGMENT $SG2413 DB 'T1', 0aH, 00H $SG2415 DB 'T2', 0aH, 00H _DATA ENDS PUBLIC main EXTRN printf:PROC pdata SEGMENT $pdata$main DD imagerel $LN5 DD imagerel $LN5+87 DD imagerel $unwind$main pdata ENDS xdata SEGMENT $unwind$main DD 010401H DD 06204H ; Function compile flags: /Odtp ; File c:\test\c\junk.c xdata ENDS _TEXT SEGMENT a$ = 32 main PROC ; 3 : void main( void ) { $LN5: sub rsp, 56 ; 00000038H ; 4 : int a = 10; mov DWORD PTR a$[rsp], 10 ; 5 : if( a == a-- ) printf( "T1\n" ); mov edx, DWORD PTR a$[rsp] mov ecx, DWORD PTR a$[rsp] mov eax, DWORD PTR a$[rsp] sub eax, 1 mov DWORD PTR a$[rsp], eax cmp edx, ecx jne SHORT $LN2@main lea rcx, OFFSET FLAT:$SG2413 call printf $LN2@main: ; 6 : if( a == --a ) printf( "T2\n" ); mov eax, DWORD PTR a$[rsp] sub eax, 1 mov DWORD PTR a$[rsp], eax mov eax, DWORD PTR a$[rsp] cmp DWORD PTR a$[rsp], eax jne SHORT $LN1@main lea rcx, OFFSET FLAT:$SG2415 call printf $LN1@main: ; 7 : } xor eax, eax add rsp, 56 ; 00000038H ret 0 main ENDP _TEXT ENDS END

      The interesting thing about that is that for the post decrement, it loads the value of a into two registers (edx & ecx), then loads it a third time in to eax, where it decrements it and then writes it back. Only then does it compare hte first two values and branch accordingly.

      And for the predecrement, it loads one register, decrements it and then saves it back before loading the modified value back into the same register and the comparing it with the stored value and branching accordingly.

      That's before the optimiser takes a look. After the optimiser, all those shenanigins are optimised away and all that remains is the printfs:

      ; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00 +.21022.08 include listing.inc INCLUDELIB LIBCMT INCLUDELIB OLDNAMES _DATA SEGMENT $SG2478 DB 'T1', 0aH, 00H $SG2480 DB 'T2', 0aH, 00H _DATA ENDS PUBLIC main EXTRN printf:PROC pdata SEGMENT $pdata$main DD imagerel $LN5 DD imagerel $LN5+35 DD imagerel $unwind$main pdata ENDS xdata SEGMENT $unwind$main DD 010401H DD 04204H ; Function compile flags: /Ogtpy ; File c:\test\c\junk.c xdata ENDS _TEXT SEGMENT main PROC ; 3 : void main( void ) { $LN5: sub rsp, 40 ; 00000028H ; 4 : int a = 10; ; 5 : if( a == a-- ) printf( "T1\n" ); lea rcx, OFFSET FLAT:$SG2478 call printf ; 6 : if( a == --a ) printf( "T2\n" ); lea rcx, OFFSET FLAT:$SG2480 call printf ; 7 : } xor eax, eax add rsp, 40 ; 00000028H ret 0 main ENDP _TEXT ENDS END

      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority". I'm with torvalds on this
      In the absence of evidence, opinion is indistinguishable from prejudice. Agile (and TDD) debunked
        Whether the stack or a register is used depends on a number of factors. Whether the stack or a register is used is not important here. Fixed.
Re: Help me to understand increment / decrement operator behavior in perl
by Anonymous Monk on Mar 04, 2015 at 10:16 UTC