一直对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本身处于跨平台的特性,在刻意弱化底层对编程的影响,都没有提供直接对于内存操作的方法,没有这种预处理也就没什么可说的了。



不需要要求两种语言具有同样一个机制,而是各有千秋,所以这两者不是一对一的替换关系,只是语法的相似说明不了任何问题,更为重要的是理解不同语言在设计思想上的不同,如果不同语言都想表示同一个事情,都想要包含有相同的特性,就用一种语言就可以了呀,这样的话那就太局限了 :)