閱讀897 返回首頁    go 阿裏雲 go 技術社區[雲棲]


C和CPP的區別 & C++,Java and Python的區別

今天在論壇上看到兩個學神討論C++的優劣性引申出來的各種問題,深深感覺自己差距很大,現就部分問題做個小的總結。

C和CPP的區別:

1. C沒有bool類型。布爾類型是int。0是假,非0是真。 
  
2. C裏字符常量(如'a'、'\n'、'\0'等)是int型,而C++裏是char型。但這不影響使用。 
  
3. 沒有引用類型的變量,一般使用指針。函數的參數也不能是引用類型,想要副作用請用指針。 
  
4. 沒有模版。可以用宏代替。但是C99開始支持inline,用法和C++一樣。 (C99 is an informal name of ISO/IEC 9899:1999, a past version of the C programming language standard)
  
5. 結構的類型是struct xxx,而不是xxx。如果你定義struct foo {int bar}; 那麼你應該用sruct foo foo1;來創建變量foo1,而不是用C++的語法foo foo1;。如果願意,你可以typedef一下。typedef struct foo foo_t; foo_t foo1;。 
  
6. 沒有運行時類型機製,沒有typeinfo,沒有dynamic_cast。你自己知道對象是什麼類型 
你可以把類型編碼到對象裏麵。 
  
一般用tagged union思路:struct Value {int tag; union {int i; double d;} val;};,自己用tag標記是什麼類型。 
  
也可以將一個公共的struct作為頭;struct Animal {int kind}; struct Cat { struct Animal header; int extra_field;}; 
  
7. 沒有麵向對象編程。你需要自己實現多態。一般用函數指針。struct Animal {void (*speak)();}; 或者讓對象指向一個虛函數表,避免每個對象裏存大量的指針,但每次調用虛函數都會多一次內存訪問。struct AnimalFuncs { void (*speak)()} cat_funcs, dog_funcs; struct Animal { struct AnimalFuncs funcs;}; struct Cat { struct Animal header; } cat1; ((struct Animal*)(&cat1))->funcs = cat_funcs; 所以,調用一次虛函數要訪問多少次內存,你永遠知道。 
  
8. 沒有namespace,你要自己在函數前麵加前綴,以避免衝突。 
  
*想一想,如果C++使用了namespace,那麼編譯出來的可執行文件裏,符號表(函數名對應函數位置的表)中某個函數的名字應該怎麼寫(這可是關係到鏈接的時候能不能找到這個函數哦)?可能不同的namespace裏有重名的函數,同一個也有重載的同名函數。就是這個簡單的“符號表怎麼寫”的問題就夠c++程序員折騰的。[二進製兼容性問題,C++的硬傷] 

*二進製兼容性 Application Binary Interface Transition(ABI Transition), 簡單的說就是當程序已經編譯完成產生了一個二進製文件,編譯過程中依賴的某些頭文件或者庫文件,當這些文件發生更新升級時,二進製還能否正常運行?這裏指的是動態庫連接的二進製文件。
  
9. 沒有string庫,但是有string.h提供了字符串基本操作,你能想象到的幾乎都有。C習慣用char型數組表示字符串,字符串以'\0'結尾。像其它函數傳參數的時候,一般傳指針,而不是拷貝字符串本身。如果你堅持要拷貝,請用strdup,並用free()回收內存。 
  
10. 沒有iostream庫,但有stdio.h。輸出可以用printf,輸入用scanf。文件操作用fopen,fread,fwrite,fclose,fseek,ftell,feof等,也有fprintf和fscanf。cout對應全局變量stdout,cin對應全局變量stdin,cerr對應stderr。你也可以直接用操作係統提供的open, read, write, close等調用。 
  
11. 沒有new和delete。你可以使用malloc和free。它們在stdlib.h中。同樣,小心內存碎片。 
  
12. 沒有stl。但是如果你用C,大概說明你需要自己設計適合自己的應用的簡單、專用而高效的數據結構。如果你用到了極其複雜的數據結構,說明你應該使用別的編程語言。 
  
p.s. Linux內核裏的環形鏈表和紅黑樹實現不錯。但是試試b-tree會不會更快。 
  
13. 你可以使用大量的第三方庫和係統調用來彌補C語言標準庫小的缺憾。正則表達式、網絡、圖形界麵、數據庫、國際化什麼的都有。但是留意:有可能你需要換一個語言。不是所有的事都適合C做。畢竟C的定位是係統編程語言。 

C++,Java and Python 

1. 都是編程語言 
2. 都是圖靈完備的編程語言 
3. 都是命令式語言 
4. 都是麵向對象的語言 
5. 都是靜態類型語言 
6. 都有大量的實際項目使用這兩種語言 
7. 都對學術界和工業界產生了巨大的影響 
  
類型係統 
  
8. 都是靜態類型語言。每個變量都有一個編譯時就知道的類型。 
    * 與之相反,Python是動態類型語言,變量都是引用,變量沒有類型,但值有類型。 
  
9. 基本數據類型都接近機器,如基本的整數、浮點數。C++的不必說,Java的int和long都是簡單的32位和64位整數,float和double是IEEE754浮點數,boolean使用int存儲,但boolean數組按每字節一個變量壓縮存儲。還有一個隱藏的“地址”類型,隻有JVM能看到。 
    * 與之相反,Python的基本類型,如int,bool,str,都是對象。任何整數都是一個對象,有屬性。 
  
10. 與第9條想對應的,基本運算符很接近機器的指令。如加減乘除、邏輯、移位、賦值等。Java甚至區分算術右移“>>>”和邏輯右移“>>”兩種不同的右移運算。C++對應的分別是無符號整數和有符號整數的右移。 
    * 與之相反,Python的基本運算符都按方法實現。如1+2,實際上是調用1.__add__(2)方法。__add__方法可以被對象重寫,以實現特殊的“加法”,如字符串相加。 
  
11. 都有基於類的複合類型係統。都支持麵向對象編程的方法,包括繼承、封裝、多態。且都有一定的運行時類型信息(C++的typeinfo和dynamic_cast,Java的反射)
    * 與之相反,C語言沒有類,在運行時也不知道一個指針指向什麼類型的對象。 
  
語句 
  
12. 都有基本的結構化編程。順序結構、選擇結構(if, switch)、循環結構(while, do-while,for) 
    * 早期的一些語言依賴GOTO。 
  
13. 都有異常處理語法try-catch 
    * C沒有異常處理 
    * Java有finally而C++依賴於棧上對象的析構函數。 
  
14. 都有函數(Java稱為方法),都支持函數的遞歸調用。 
    * 早期的Fortran不支持遞歸調用,有的語言沒有函數。 
  
15. 都支持按值傳遞。 
    * C++支持按引用傳遞。你可以將局部變量的“引用”傳給函數,以改變變量的值。 
    * Java本質上隻支持按值傳遞。引用類型可以認為引用本身是值,但是不能將局部變量“按引用傳遞”以改變局部變量。 
    * Python本質上隻支持按值傳遞,原理和Java一樣,可以理解為“引用”本身是值。而Python所有變量都是引用,甚至包括所有的整數,如42,也是將“42”這個對象的引用傳入。但是整數、字符串等都是“不可變”(immutable)的類型,a=2; a=3實際上是創建一個新的對象“3”,把引用賦給a,而“2”這個對象仍然是2。你不能通過參數讓另一個函數改變當前函數的局部變量。 
    * Haskell的所有函數調用都是按名傳遞。如果參數是一個表達式,那麼這個參數不到需要求值的時候是不會求值的。而C++,Java,Python都必須先求參數值,後傳入函數。 
  
麵向對象編程 
  
16. 都有泛型係統。C++基於模板,為每種類型生成代碼;而Java的泛型隻在編譯時用於檢查,編譯到JVM上則使用無類型的引用,運行時看不見類型參數。 
     * C沒有。可以用void*指向任何東西。 
  
內存管理 
  
17. 都支持手動控製生存周期的內存管理(new),但C++要求手動回收(C++11有例外)。 
  
實現 
  
18. 編譯:一般都是先編譯,後執行。但C++是編譯為本地代碼,而Java一般先編譯成bytecode。然後由JVM決定是解釋執行還是編譯成本地代碼執行。 
     * Python, Ruby, JavaScript, PHP等腳本語言一般不預先編譯,但運行時可以采用JIT Compiling策略。Haskell既可以解釋又可以編譯。LISP的執行過程就是源代碼(S-Exp)的擴展、變換、展開的過程。 
  
19. 發布:一般都以二進製發布,但C++的二進製是本地代碼,而Java是bytecode。 
     * Python一般直接發布源代碼。罕見的一些閉源軟件會發布bytecode。 
     * PHP也是。源代碼直接嵌在網頁代碼中(或者說網頁代碼嵌在PHP代碼中,怎麼說都行) 
     * JavaScript也是,直接用源代碼讓瀏覽器解釋。 
     * Haskell既可以解釋,也可以編譯,如何發布都行,看需要。 
  
20. 動態裝載:依賴於實現,但C++、Java一般都支持。C++使用動態鏈接庫,Java可以動態裝載類。 
  
21. 外語接口:都可以與C語言交互。C++可以用extern "C"。Java需要JNI。 
  
都不具備的特性 
  
22. Tagged Union數據類型:帶標記的聯合類型,在函數式語言中很常見。但C++和Java可以用類和繼承來模擬,也可以直接用if/else。 
     * Scala支持Case Class 
     * Haskell、ML的基本數據類型 
  
23. 模式匹配(Pattern Matching)結構,在函數式語言中很常見。但C++可以用dynamic_cast,Java可以用instanceof檢測指針/引用的目標類型。 
     * Scala有match-case語句和unapply方法 
     * Ruby有case-when語句和模式匹配運算符“=~” 
     * Haskell、ML都有此類語句 
  
24. 複雜查詢語句 
     * Python有List Comprehension。類似[len(x) for x in names if x.startswith("S")] 
     * Scala有For Comprehension。類似for(x <- names; if x.startswith("S")) yield x.size; 
     * C#有LINQ。類似from x in names where x.StartsWith("S") select s.length 
     * Ruby沒有此類語句,但是ruby的函數調用很容易用塊串接。 
  
25. lambda函數(C++11有例外)、閉包(C++11有例外)和高階函數:將一個函數作為另一個函數的參數,即所謂“高階函數”(C++是支持的,但在沒有閉包的情況下很吃虧)。 
     * Python:filter(lambda x: x%2==0, [1,2,3,4,5,6,7,8,9,10]) 
     * JavaScript:connection.on_data(function(d) { buffer.add(d);}); 
     * Java可以用接口和對象來模擬函數作為參數:window.addEventListener(new EventListener(){@Override void onShow() { window.setTitle("Hello"); }}); 
  
26. coroutine(C++可以依賴庫和係統調用實現) 
     * C:使用swapcontext或者longjmp模擬 
     * Python:幾乎所有的for循環都依賴coroutine 
     * Ruby:Fiber 
     * JavaScript:Firefox17開始有實驗性支持,但沒有標準化。 
  
27. 動態對象:將一個對象的所有方法調用抽象成“方法名+參數”而不是具體的某個函數和參數。允許你做一些匪夷所思的事,比如將一個對象做“代理”,對它的所有方法調用將被記錄方法名和參數,通過網絡發送到遠端,再將傳回的數據翻譯成基本數據類型作為返回值。 
     * Ruby裏所有的對象都是這樣做的。 
     * Python可以用__getattr__實現這種行為。Python用這種方法實現了XML-RPC。 
     * Java可以用java.lang.reflect.Proxy實現這個,也可以借助CGLib。 
  
28. 擴展方法:給一個類添加一個新的方法 
     * C#有extension method。 
     * Scala可以使用隱式類型轉換,轉換成一個封裝類型,以造成“添加一個新方法”的假象。 
     * Haskell可以定義新的type class,將若幹個已有的但不相幹的類都定為成員。 
     * Ruby允許你隨時重新打開一個類,隨便加新方法。 
  
29. 嵌入式語言:作為另一個程序的一部分,用於程序擴展語言。顯然C++和Java都不是為這個而設計的。 
     * Lua的解釋器很小,適合嵌入另一個程序(如魔獸世界) 
     * Python相對大一些,但也適合做嵌入式腳本語言。 

最後更新:2017-04-03 15:21:43

  上一篇:go dm642的中斷定時器
  下一篇:go Makefile的規則