2011-08-19 60 views
14

假設我在C代碼中有這樣的東西。我知道你可以使用#define來代替編譯器,但是出於好奇心,我問編譯器是否也會把這件事情弄清楚。編譯器會優化這個

我認爲這對Java編譯器來說更重要,因爲它不支持#define

const int CONDITION = 0; 
........ 
// Will the compiler compile this? 
if (CONDITION) 
{ 

} 
....... 
+13

你爲什麼不嘗試一下,看看產生的二進制文件? –

+2

特別是,您可以在您的Class上使用javap -c命令來打印出字節碼,這實際上很容易閱讀。 http://download.oracle.com/javase/1,5.0/docs/tooldocs/windows/javap.html – Brigham

+2

我記得把'if(false){...}'作爲推薦的排除Java代碼的方法,在C預處理器中使用'la'#if'。那是幾年前。 – Joe

回答

20

在Java中,代碼if甚至不是編譯代碼的一部分,它必須編譯,但不會寫入已編譯的字節碼,它實際上取決於編譯器,但我不知道編譯器不會優化。它的規則在the JLS定義:

優化編譯器可能會意識到語句x = 3;將永遠不會 被執行和毫安y選擇從 生成的類文件中省略該語句的代碼,但是語句x = 3;在這裏指定的技術意義上不被視爲 「無法達到」。

這種區別對待的理由是讓程序員 定義「標誌變量」,如:

static final boolean DEBUG = false; 

,然後編寫代碼,如:

if (DEBUG) { x=3; } 

的想法是,它應該可以將DEBUG 的值從false更改爲true或從true更改爲false,然後正確編譯代碼 ,而不對程序文本進行其他更改。

不知道C.

+0

Java SE 9的更新鏈接:https://docs.oracle.com/javase/specs/jls/se9/html/jls-14.html#d5e23594 –

11

首先,Java不允許非布爾在條件語句像C(ifwhile等)。另外,如果你在你的if抽查中有一個「常數」表達」,編譯器將內部警告你,你是比較相同的表現,所以我敢肯定,它優化掉了。如

final int i = 1; 
    if (1 == i) { // warning 
     System.out.println("HI"); 
    } 
+0

如果'i'不是'1',這段代碼只會產生警告(「死代碼」,Java 6)! –

0

我記得在我的Java和C#程序,它確實情景(優化出來)。但是我也知道這很大程度上取決於編譯器設置 - 因此情況太不確定。

在Java場景中,我們在一個Java源文件中使用了const值,而在另一個類(文件)中使用了它們。發生了什麼事,當我們用const值更改並重新編譯文件時,使用部分的流程中沒有任何變化。我們必須重新編譯整個項目(這是它被優化的證明)。

6

不要問這樣簡單的問題(其中唯一正確的答案是「用你的編譯器試一試」) - 爲什麼不試試呢?

public class Test { 
    public static void main(String[] args) { 
     if (true) { 
      System.out.println("Yep"); 
     } 
     boolean var = false; 
     if (var) { 
      System.out.println("Nope"); 
     } 
     final boolean var2 = false; 
     if (var2) { 
      System.out.println("Nope"); 
     } 
    } 
} 

javac .\Test.java 
javap -c Test 
Compiled from "Test.java" 
public class Test { 
    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: getstatic  #2     // Field java/lang/System.out:Ljava/io/PrintStream; 
     3: ldc   #3     // String Yep 
     5: invokevirtual #4     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     8: iconst_0 
     9: istore_1 
     10: iload_1 
     11: ifeq   22 
     14: getstatic  #2     // Field java/lang/System.out:Ljava/io/PrintStream; 
     17: ldc   #3     // String Yep 
     19: invokevirtual #4     // Method java/io/PrintStream.println:(Ljava/lang/String;)V 
     22: return 
} 

你不需要知道很多關於Java/C#字節碼或組件能夠理解這是怎麼回事。現在去C#試試。

0

下面是具體到C語言。我不知道Java如何處理它。

由於int被定義爲const,if (i)在這裏變成了no-op指令。一個聰明的編譯器應該能夠優化掉那個空的if語句。

:VC 2008

非空{}if聲明:

const int i = 1; 
// mov dword ptr [i], 1 
if (i) 
// mov eax, 1 
// test eax, eax 
// je wmain+35h 
{ 
    int j = 2; 
    // move dword ptr [j], 2 
} 
// .. 

{}if聲明:

const int i = 1; 
// mov dword ptr [i], 1 
if (i) 
{ 
} 
// .. 
2

我只是做了一個快速檢查與以下一段代碼爲

public class Test { 
    private static final boolean flag = true; 

    public static void main(String[] args) throws InterruptedException { 

     if(flag){ 
      System.out.println("1"); 
      System.out.println("1"); 
      System.out.println("1"); 
      System.out.println("1"); 
      System.out.println("1"); 
      System.out.println("1"); 
      System.out.println("1"); 
      System.out.println("1"); 
      System.out.println("1");   
     } 

    } 

} 

當標誌=真,將得到的類文件的大小是708

當標誌=假。結果類文件的大小是462

這意味着編譯當然有,優化靜態最終值

+0

這是否與標準的JDK 1.6兼容 – Santosh

+1

轉載於GCC 4.4.5 (C代碼),它有類似的結果。 – Jonathon

0

Java編譯器必須檢測顯然無法訪問的代碼,這是一個語言要求。味精是最後但編譯器既不抱怨味精沒有初始化,也不抱怨,它就會被初始化兩次

static final boolean flag = true; 

public static void main(String[] args) { 
    final String msg; 
    if (flag) 
     msg = "true"; 
    if (!flag) 
     msg = "false"; 
    System.out.println(msg); 
} 

注:所以下面的代碼將編譯沒有錯誤。大多數編譯器不會將死代碼寫入類文件。但即使如此,JIT也會優化它。

C++也有一個編譯時間常量的概念。 const int是一個編譯時間常量,所以它可以用作非類型模板參數。因此,即使編譯時沒有指定優化選項,每個理智的C++編譯器都會檢測並優化該類型的明顯死代碼。