今天遇到同学提出了一个题目,如下:
下列程序输出结果为:
#include<stdio.h>
int main(){
char a[] = "123" ,*p;
p = a ;
printf("%c%c%c\n", *p++,*p++,*p++);
return 0 ;
}
我觉得是111,但是实际上结果为123
我对C和C++了解并不是很深入,解释不了原因,然后我去用java试了试。
这道题我们主要的错误点在于对于++的理解,我就在java中只测试++,忽略题中对于指针的考察。
代码如下:
public class App
{
public static void main( String[] args )
{
int i = 1;
System.out.println(i+++","+i+++","+i++);
}
}
我认为会是1,1,1但还是1,2,3。其实之前只是知道i++是后加,但并不知道是在什么后加一。
于是去看一下字节码探个究竟:
上面程序的字节码如下:
Classfile /Users/zjchai/CodeSpace/Workspace/Maven/TestArchetype/target/classes/jack/test/App.class
Last modified 2016-3-23; size 768 bytes
MD5 checksum ea64c6e591bb2f46ea1f8ddc43509c91
Compiled from "App.java"
public class jack.test.App
minor version: 3
major version: 45
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #11.#27 // java/lang/Object."<init>":()V
#2 = Fieldref #28.#29 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Class #30 // java/lang/StringBuffer
#4 = Methodref #3.#27 // java/lang/StringBuffer."<init>":()V
#5 = Methodref #3.#31 // java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;
#6 = String #32 // ,
#7 = Methodref #3.#33 // java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
#8 = Methodref #3.#34 // java/lang/StringBuffer.toString:()Ljava/lang/String;
#9 = Methodref #35.#36 // java/io/PrintStream.println:(Ljava/lang/String;)V
#10 = Class #37 // jack/test/App
#11 = Class #38 // java/lang/Object
#12 = Utf8 <init>
#13 = Utf8 ()V
#14 = Utf8 Code
#15 = Utf8 LineNumberTable
#16 = Utf8 LocalVariableTable
#17 = Utf8 this
#18 = Utf8 Ljack/test/App;
#19 = Utf8 main
#20 = Utf8 ([Ljava/lang/String;)V
#21 = Utf8 args
#22 = Utf8 [Ljava/lang/String;
#23 = Utf8 i
#24 = Utf8 I
#25 = Utf8 SourceFile
#26 = Utf8 App.java
#27 = NameAndType #12:#13 // "<init>":()V
#28 = Class #39 // java/lang/System
#29 = NameAndType #40:#41 // out:Ljava/io/PrintStream;
#30 = Utf8 java/lang/StringBuffer
#31 = NameAndType #42:#43 // append:(I)Ljava/lang/StringBuffer;
#32 = Utf8 ,
#33 = NameAndType #42:#44 // append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
#34 = NameAndType #45:#46 // toString:()Ljava/lang/String;
#35 = Class #47 // java/io/PrintStream
#36 = NameAndType #48:#49 // println:(Ljava/lang/String;)V
#37 = Utf8 jack/test/App
#38 = Utf8 java/lang/Object
#39 = Utf8 java/lang/System
#40 = Utf8 out
#41 = Utf8 Ljava/io/PrintStream;
#42 = Utf8 append
#43 = Utf8 (I)Ljava/lang/StringBuffer;
#44 = Utf8 (Ljava/lang/String;)Ljava/lang/StringBuffer;
#45 = Utf8 toString
#46 = Utf8 ()Ljava/lang/String;
#47 = Utf8 java/io/PrintStream
#48 = Utf8 println
#49 = Utf8 (Ljava/lang/String;)V
{
public jack.test.App();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Ljack/test/App;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: iconst_1
1: istore_1
2: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
5: new #3 // class java/lang/StringBuffer
8: dup
9: invokespecial #4 // Method java/lang/StringBuffer."<init>":()V
12: iload_1
13: iinc 1, 1
16: invokevirtual #5 // Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;
19: ldc #6 // String ,
21: invokevirtual #7 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
24: iload_1
25: iinc 1, 1
28: invokevirtual #5 // Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;
31: ldc #6 // String ,
33: invokevirtual #7 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
36: iload_1
37: iinc 1, 1
40: invokevirtual #5 // Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;
43: invokevirtual #8 // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
46: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
49: return
LineNumberTable:
line 7: 0
line 8: 2
line 9: 49
LocalVariableTable:
Start Length Slot Name Signature
0 50 0 args [Ljava/lang/String;
2 48 1 i I
}
SourceFile: "App.java"
这个挺烦的,我们只看public static void main(java.lang.String[]);之后的code结合上面的Constant pool。
按照代码序号依次解释什么意思:
0:将1入操作数据栈;
1:将栈顶元素保存到第一个临时变量(i);
2,5不解释
8:是为9的构造函数准备的;
12:将第一个临时变量(i)的值入栈;
13:将第一个临时变量加1,不影响栈,即在临时变量区自加;
16,19,21:不解释了,就是执行append方法把栈顶数据加进去此时这个数值为1,局部变量区的数值是2了已经;
24:即又是把第一个临时变量(i)的值(2)入栈;
25:又是局部变量的第一个元素(i)自己加一变成3了,且不影响栈;
。。。
我相信下面就都会了。
因此我们可以看到,++为后缀的话,从字节码的角度上看是执行完load之后加一。如果为前缀我试了,就是在load之前,有兴趣可以自己试一下。