C language & Java parsing: when i = i + +, what is the result?

Posted by headbangerbuggy on Mon, 03 Jan 2022 00:51:11 +0100

preface:

A friend discussed a problem with me before. He asked in java, i=1;i=i++; At first, I thought the result was 2, but when he said that the answer was 1, I had to think of a reasonable explanation. I think it may be because the temporary variable is incrementally changed after I is assigned to I, so the value of I has not changed. When I was satisfied with my explanation, they said that the answer in c language was 2. Well, I can only say that the implementation of the compiler is different. Of course, the answer is too unconvincing, so the task of finding the answer began.

i first saw this explanation in c++ primer: "the efficiency of + + i is higher than that of i + +, because i + + must have a temporary variable to store the value of i itself "Well, i was only right about the temporary variable, but the reason is that the temporary variable is first used to store the value of i, then i itself increases, and then the temporary variable is assigned to the left. At this time, the value of i is overwritten by its previous value, so it becomes 1 again, but this is just a speculation. Besides, why is 2 in c? So i have to decompile...

The jdk of Java comes with a tool, javap, which can be decompiled. We use the "jvm version code" generated by the java compiler. I will write two pieces of code for comparison. One is i=i + +; A common j=i + +; Note that I use 2 as the initial value (that is, it will be automatically increased to 3). Because 1 is too special, its variable names can be seen everywhere in the bytecode, and + + is not added with 1, so I use 2 as the initial value, which is also convenient to find.

publicclassTest{

publicstaticvoidmain(String[] args){

inti =2;

  i = i++;

  System.out.println(i);

  }

}

The bytecode of javap -c Test} is as follows:

public class Test extends java.lang.Object{

public Test();

  Code:

  0: aload_0

  1: invokespecial #1; //Method java/lang/Object."<init>":()V

  4: return

public static void main(java.lang.String[]);

  Code:

  0: iconst_2

  1: istore_1

  2: iload_1

  3: iinc 1, 1

  6: istore_1

  7: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;

  10: iload_1

  11: invokevirtual #3; //Method java/io/PrintStream.println:(I)V

  14: return

}
0:  iconst_2  //Stack the constant 2 of type int

1:  istore_1  //Store int value in variable 1

Is to define and initialize the value of i.

The key is here:

2:  iload_1  //Save the value of variable 1

3:  iinc  1, 1 //Increase the value of variable 1 by itself (i is now 3)

6:  istore //Put the previously saved value into variable i (i now becomes 2 again.)

Actually in 2: iload_1, the value of the i variable is placed on the stack, which is what we call temporary storage.. After that, he was assigned again

Look at Test2 and you will see that it works normally:

publicclassTest2{

publicstaticvoidmain(String[] args){

inti =2;

intj =0;

  j = i++;

  System.out.println(j);

  }

}

The bytecode of javap -c Test2} is as follows:

public class Test2 extends java.lang.Object{

public Test2();

  Code:

  0: aload_0

  1: invokespecial #1; //Method java/lang/Object."<init>":()V

  4: return

public static void main(java.lang.String[]);

  Code:

  0: iconst_2

  1: istore_1

  2: iconst_0

  3: istore_2

  4: iload_1

  5: iinc 1, 1

  8: istore_2

  9: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;

  12: iload_2

  13: invokevirtual #3; //Method java/io/PrintStream.println:(I)V

  16: return

}
4: iload_1  //The value of variable 1 is saved (stacked)

5: iinc  1,1  //i self increasing

8 istore_2  // Pop up the stack and assign it to variable 2..

Now you see, what happened..

But it's not over yet. We haven't explained what happened in c...

In gcc, compiling a c program requires four stages: preprocessing, compiling, assembling and linking. We stop after the compilation phase, so we will get the sink code of at & T,

We write two kinds of code, similar to the java version

Look at the normal situation first:

#include<stdio.h>

intmain()

{

inti =2;

intj =0;

    j = i++;

printf("j = %d\n",j);

return0;

}

Compiled into sink Code:

.file"test.c"

  .section .rodata

.LC0:

.string"j = %d\n"

  .text

.globl main

.typemain, @function

main:

  pushl %ebp

  movl %esp, %ebp

  andl $-16, %esp

subl$32, %esp

movl$2, 28(%esp)

movl$0, 24(%esp)

  movl 28(%esp), %eax

  movl %eax, 24(%esp)

addl$1, 28(%esp)

  movl $.LC0, %eax

  movl 24(%esp), %edx

  movl %edx, 4(%esp)

  movl %eax, (%esp)

callprintf

movl$0, %eax

  leave

  ret

  .size main, .-main

.ident"GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3"

.section.note.GNU-stack,"",@progbits

Including movl $ Lc0,%eax# and subsequent statements are related to output.

We will remove the initialization related. The key parts are:

movl    28(%esp), %eax

movl    %eax, 24(%esp)

addl$1, 28(%esp)

Save the value of i (put it in eax), then put it in j, and i will increase by itself, which is very consistent with the answer in your mind. If i=i + +, naturally i will be assigned and increase by itself... That is the answer in your mind, but is it really so? Let's continue to see....

#include<stdio.h>

intmain()

{

inti =2;

i = i++;

printf("i = %d\n",i);

return0;

}

Corresponding sink Code:

.file"test2.c"

  .section .rodata

.LC0:

.string"i = %d\n"

  .text

.globl main

.typemain, @function

main:

  pushl %ebp

  movl %esp, %ebp

  andl $-16, %esp

subl$32, %esp

movl$2, 28(%esp)

addl$1, 28(%esp)

  movl $.LC0, %eax

  movl 28(%esp), %edx

  movl %edx, 4(%esp)

  movl %eax, (%esp)

callprintf

movl$0, %eax

  leave

  ret

  .size main, .-main

.ident"GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3"

.section.note.GNU-stack,"",@progbits

The key part is to save ADDL $1,28 (% ESP). This is.... Look at my test3 C right...

#include<stdio.h>

intmain()

{

inti =2;

i++;

printf("i = %d\n",i);

return0;

}

Generated sink Code:

aiqier@aiqier-laptop:~/c/test3$ cat test3.s

.file"test3.c"

  .section .rodata

.LC0:

.string"i = %d\n"

  .text

.globl main

.typemain, @function

main:

  pushl %ebp

  movl %esp, %ebp

  andl $-16, %esp

subl$32, %esp

movl$2, 28(%esp)

addl$1, 28(%esp)

  movl $.LC0, %eax

  movl 28(%esp), %edx

  movl %edx, 4(%esp)

  movl %eax, (%esp)

callprintf

movl$0, %eax

  leave

  ret

  .size main, .-main

.ident"GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3"

.section.note.GNU-stack,"",@progbits

Well, in c language, i=i + +; With i + +; Like the assembly code of, according to the normal logic (our previous analysis), i = 2; i=i + +; the value of i is 3, which is no problem. However, gcc will optimize the compiler, so save the value of i and assign it to i. these two statements are naturally chicken ribs and are optimized.

In summary, we found that for the temporary values saved by i + +, in java, they are automatically incremented after returning the temporary value, while in c language, they are automatically incremented after returning the temporary value. Therefore, this is why i=i + + has different results in these two languages. c# i don't know. Interested students can try python. There is no + +. Hehe, if my analysis is wrong at the beginning, i hope to discuss it with you.

In addition, for most of our friends now, learning programming technology is the most important! The best time to plant a tree is ten years ago, followed by now. For those who are ready to learn programming, if you want to better improve your core programming ability (internal skill), you might as well start now!

C language c + + programming learning and communication circle, QQ group: 614504899[ Click to enter WeChat official account: C language programming learning base

Sorting and sharing (source code, project practice video, project notes, basic introductory tutorial)

Welcome to change careers and learn programming partners. Use more materials to learn and grow faster than you think!

Topics: Java C Programming