一直對c語言的預處理部分沒有一個系統的學習,最近看《c專家編程》這本書,里邊提到了一些用法,索性對預處理這一部分總結一下。
首先是一些定義:
預處理指令是以#號開頭的代碼行。#號必須是該行除了任何空白字符外的第一個字符。#后是指令關鍵字,在關鍵字和#號之間允許存在任意個數的空白字符。整行語句構成了一條預處理指令,該指令將在編譯器進行編譯之前對源代碼做某些轉換。下面是部分預處理指令:
指令 用途# 空指令,無任何效果#include 包含一個源代碼文件#define 定義宏#undef 取消已定義的宏#if 如果給定條件為真,則編譯下面代碼#ifdef 如果宏已經定義,則編譯下面代碼#ifndef 如果宏沒有定義,則編譯下面代碼#elif 如果前面的#if給定條件不為真,當前條件為真,網站空間,則編譯下面代碼,其實就是else if的簡寫#endif 結束一個#if……#else條件編譯塊#error 停止編譯并顯示錯誤信息
然后有一些固定的調用,例如
__LINE__ 雙下劃線,而不是單下劃線 。 由于編譯器會默認的包含一些標準庫文件,美國服務器,所以這些宏定義會默認的存在。可以直接使用 __FILE__ 包含當前程序文件名的字符串 __LINE__ 表示當前行號的整數 __DATE__ 包含當前日期的字符串 __STDC__ 如果編譯器遵循ANSI C標準,它就是個非零值 __TIME__ 包含當前時間的字符串#include<stdio.h>int main(){ PRintf("Hello World!/n"); printf("%s/n",__FILE__);//當前的文件名 不包括文件路徑 printf("%d/n",__LINE__); //當前這句話所處的行號 包括空白行 return0;}
我體會宏替換,一方面是為了閱讀的方便,例如使用#define將一些常量替換為便于記憶的大寫表示,或者是用#typedef進行一些類型的替換;另一方面是為了告訴編譯器選擇性處理一些代碼,使程序在編譯生成目標文件的時候根據不同情況選擇不同部分的代碼,讓效率提高。
1、看的過程熟悉了預處理部分的#define等等的處理,腦子里冒出一個想法,為什么java中沒有類似的處理?原因是什么?想實現類似的處理應該怎么去做?
從編譯的過程能看出原因,因為java沒有預處理過程。c語言的編譯過程是這樣的 C源程序->編譯預處理->編譯->優化程序->匯編程序->鏈接程序->可執行文件。預處理過程會對于宏定義、根據條件決定編譯時是否包含某些代碼,以及空白字符和注釋進行替換和處理,這一步是獨立在編譯過程之外的,編譯程序不會處理這些內容。
而對于java來說,編譯的全過程就是,編譯-》字節碼-》解釋執行。在編譯的過程,會進行一些常量替換,去掉注釋的工作。
stackoverflow上提到的類似功能的實現方法:
A 'static final' of a primitive or String type is defined by the Java compiler spec (not JIT) to be a constant. It is not "inlined" in the sense of JIT, it is actually directly compiled into that part of the code just like a #define is in C.
java中聲明為static final的基本類型或者String類型的變量,會在編譯階段直接用常量值去替代變量的使用。那么如果出現了聲明成static final的class對象呢?
可以自己編程序來驗證
最簡單的把一下幾個class定義放到三個java文件中,
package test.finalordefine;
public class TestDefine {
public static final int INT_VALUE = 1;
public static final DisplayValue VALUE = new DisplayValue("X");
}
package test.finalordefine;
public class DisplayValue {
private String a;
public DisplayValue(String des) {
this.a = des;
}
public String toString(){
return this.a;
}
}
package test.finalordefine;
public class test {
public static void main(String[] args) {
System.out.println("Int = " + TestDefine.INT_VALUE);
System.out.println("Value = " + TestDefine.VALUE);
}
}
進行一次編譯,運行的結果是 Int=1,value=x;這時候再改變TestDefine中的兩個成員的值,免備案空間,例如int_value=2,value = "Y" 再編譯一下TestDefine,這時運行test打印的仍然是Int=1,但Value變為了Y;說明將Test編譯的時候,基本類型的int被當成了常量直接進行了替換,而DisplayValue類型的變量盡管封裝了String類型,仍然是以引用方式替換的。
當然這一切在eclipse是看不出來的,需要單個編譯才能看出。
*-------------END---------------*
另外,typedef 這種實現類型的替換功能,在java中就是徹底沒有了。
有人可能說既然能把注釋過程在編譯的時候去掉,那為什么不增加宏替換的功能呢。
這種處理方式上的差異,不僅僅兩種流程和語法的不同,對于C的預處理過程,包括C本身的特性,就是要對于內存和編譯過程有著完全的控制,需要程序、編譯器、內存、寄存器按照程序猿的想法來運行,讓編譯器不要哪段代碼就不要哪段代碼,給人的感覺是在c中你可以掌握你想掌握的一切(當然這里邊也有一種蛋蛋的優越感)。
而java本身處于跨平臺的特性,在刻意弱化底層對編程的影響,都沒有提供直接對于內存操作的方法,沒有這種預處理也就沒什么可說的了。
不需要要求兩種語言具有同樣一個機制,而是各有千秋,所以這兩者不是一對一的替換關系,只是語法的相似說明不了任何問題,更為重要的是理解不同語言在設計思想上的不同,如果不同語言都想表示同一個事情,都想要包含有相同的特性,就用一種語言就可以了呀,這樣的話那就太局限了 :)語言中的宏替換和java的相似性思考
新聞熱點
疑難解答