為什麼說c,c++不能跨平臺,編譯器是在計算機操作系統上的嗎,難道說編譯器不在c,c++程序裡嗎?

曾傳新


首先我們糾正一下題目中一個小小的誤區,不是C和C++不能跨平臺,應該是說是C/C++源代碼在編譯後生成的

.exe

文件不能跨平臺,源代碼和可執行文件要區分開來。

想要搞明白這個問題,我們先得了解一下源代碼是怎麼變成程序的。

四個過程:預處理——編譯——彙編——可執行文件

當我們編寫完代碼後,源代碼會經過上述的四個環節,最終變成常見的可執行文件。

  1. 預處理階段(hello.i):在源代碼中會有頭文件,一些宏,註釋等。預處理的目的就是將頭文件展開,宏文件代換,去掉註釋等,對代碼進行一些初步的處理

  2. 編譯階段(hello.s):這一階段主要是檢查語法上的錯誤,比如內存有沒有溢出,指針有沒有指錯對象,然後生成可彙編文件。

  3. 彙編階段(hello.o):計算機是不認識代碼的,所以需要將彙編代碼轉換成0和1組成的機器碼

  4. 鏈接(a.out):鏈接有兩種情況:靜態和動態。靜態庫和應用程序編譯在一起,在任何情況下都能運行;而動態庫是動態鏈接,文件生效時才會調用。最終生成一個可執行文件。

編譯器的作用

我們不要把編譯器想的太厲害,覺得編譯器是萬能的。實際上編譯器就像是一個翻譯,負責把高級語言轉變成機器能看懂的低級語言,翻譯過程就是上述的四個過程。但是其中有一點需要格外注意。那就是不同的公司使用的指令集不同。輸出程序的格式和CPU使用的指令集有關,比如X86,arm,還有MIPS等等, 由於設計思路的差異,所以不同平臺上編譯生成後的可執行文件格式是不一樣的,可能在ubantu裡能運行的C程序,放到windows下就會報錯。

類比一下java,為什麼說java可以跨平臺,是因為java內置了一個虛擬機,程序都從虛擬機中跑的,所以有人說“java不僅是一種語言,更是一個平臺”。


綜上所述,C/C++的一些基礎性代碼是可以跨平臺的(可能會受API影響),是生成的可執行文件不能跨平臺,C/C++不自帶編譯器,不同平臺下的編譯器存在差異。

(都看到最後了,麻煩點個贊和關注吧,謝謝~)


愛思考的奧特曼


先來討論一下C&C++語言的執行過程,從而搞清楚為什麼C&C++語言不能跨平臺。

我們分階段來討論(如圖4所示):

  • 預處理階段。預處理器(cpp)來把 代碼中開頭的行進行展開, 比如頭文件,宏等內容,修改最初的C文件。
  • 編譯階段。編譯器(ccl)將修改後的C文件,翻譯成了 另一文本文件,,這就是我們所說的彙編程序了。

  • 彙編階段。彙編器(as)將翻譯成機器語言指令。 把這些命令打包成一種叫做可重定位目標程序(relocatable object program)的格式,此時的輸出格式就是了。這其實就是二進制文件了。
  • 鏈接階段。編譯過程最後還有一個鏈接階段(程序調用了 函數),最後的輸出結果還是和上一步類似,都是直接二進制文件。
圖2以上就是大家都知道的程序。最終執行的結果 就是在console上輸出一行字符串, ,如圖3,4所示,大家可以很清楚的看到X86和ARM不同的編譯結果。而在他們輸入的0和1中,有些代表的是指令,這些是有固定含義和編碼的。也是芯片能識別的。而另一些是數據。這些不同的程序的數據自然是不同的。我們前面就說,不管多麼複雜的計算機操作,到了cpu級別都是0和1,數據雖然多變,但是 指令的數量是有限的。因為 指令是要被芯片固定識別的。芯片中要用 晶體管(最初是電子管)組成的與或非門組合來識別這些指令和數據。因為直接輸入0和1,實在太繁瑣了,所以他們就發明了彙編語言。來簡化 程序的編寫。

比如 計算 1+1,兩個 數據1都 使用 來表示,而 加操作,放在cpu中,可以是 (這個是胡亂寫的),這個二進制代表的加操作能被計算機識別。而因為這個加操作對於cpu來說,編碼的格式是固定的。所以可以直接一個助記符來表示,這樣科學家們寫程序就方便多了,而這就是彙編程序的由來。因為彙編程序完成之後,可以再有一個專門的程序(就是要上文中所說的彙編器)來把編寫的彙編程序編譯成0和1.這樣計算機也可以識別了,而彙編語言本身也方便了程序的編寫和閱讀。

編寫彙編比直接編寫二進制方便高效了太多。但是 隨著計算任務的複雜,程序的規模越來越龐大,使用匯編程序也很累啊,那麼是否有更簡單的方式呢?所以科學家們發明了高級語言(比如 ,等),在編寫程序的時候,使用C語言等編寫,然後再使用 編譯器將C語言程序翻譯成彙編程序,彙編程序再使用彙編器編譯成0和1,這樣,cpu能識別的東西沒有變化,但是對於編寫程序的人,確實方便了很多。

通過以上的描述,我們就知道了高級語言的大概由來。也明白了我們所編寫的各種高級語言,到了最後,其實都是轉化為二進制執行。

而直接二進制格式的程序,我們稱之為本地機器碼(native code)。而類似那些 之類的 助記符,以及彙編的編寫格式或標準,我們稱之為 指令集

但是問題的關鍵來了。不同公司所生產的 cpu芯片。他們所使用的指令集不同啊, 這種芯片設計的事情,沒有國際統一的標準,甚至像intel所代表的複雜指令集,和arm為代表的精簡指令集,還有PowerPC、MIPS等指令集,它們指令集的設計思路就是不一樣的。

所以為什麼說C&C++語言不能實現跨平臺運行,就是因為它編譯出來 輸出文件的格式,只適用於某種特定的CPU,而其他CPU不認識啊。


鼴鼠科技


程序的可移植性,本質上與實現它的編程語言無關。需要區分兩個概念:編程語言和語言的實現。

編程語言

我們通常說的C/C++、Java、Python、JavaScript等編程語言,其實是指語法和語義上的一種規範。這種規範,定義了編寫程序時必須要遵循的一種書寫規則,只有符合規則才能被編譯器正確識別和理解。

語言的實現

語言的實現,通常指的就是編譯器了。對於同一種語言,不同的編譯器可以有完全不同的實現方式。

以Python語言為例,就有很多種大相徑庭的編譯器實現。

  1. CPython - 解釋執行。
  2. PyPy - JIT執行。
  3. Jython - 把Python源碼編譯成Java字節碼,然後再Java虛擬機裡執行。

C/C++也是一樣,有很多種不同的實現,不過目前主流的實現,全都是編譯成二進制的機器碼執行的。但是,其實網絡上也有很多C/C++的解釋器,就是直接把C/C++的源碼解釋執行的。

我目前正在開發中的一個編譯器項目,就支持C語言的絕大部分語法,目前的實現,也是解釋執行的。

瞭解了編程語言和語言的實現後,再來說說程序的可移植性。

程序的可移植性

程序的可移植性,通俗來講,就是你在一個環境上編寫出來的代碼,是不是能夠很方便的把它放到另外一個環境上運行。這裡的環境,包括軟件環境和硬件環境。

  • 軟件環境:通常指操作系統,以及程序所依賴的運行時環境,如系統支持庫等。
  • 硬件環境:指程序運行的目標硬件,包括CPU、芯片組、外設等。

但從編程語言本身來說,理論上,任何高級編程語言都是可移植的,只不過實現可移植性的方式有所不同。

編寫一次,到處編譯

對於C/C++,目前幾乎所有通用的實現都是直接把源碼編成二進制目標代碼。對於這種實現方式,要想進行移植,就必須要保證在不同的軟件環境和硬件環境下,都要實現相應的編譯器。比如要有Windows+x86的編譯器、Linux+x86的編譯器、Linux+ARM的編譯器等等。也就是編寫一次,到處編譯。

所以,所謂的C/C++程序不能跨平臺,是指被編譯成二進制的可執行文件不能跨平臺,而不是C/C++語言本身不能跨平臺。

編譯一次,到處運行

而Java則選擇了不同的實現方式。Java典型的實現是,把Java源碼編譯成字節碼,然後把字節碼放到虛擬機中進行執行。

對這種方式,要實現可移植性,則必須要保證,在每種不同的軟件環境和硬件環境中,都必須要實現相應的Java虛擬機。

比如要有Windows+x86的Java虛擬機,Linux+x86的Java虛擬機、Linux+ARM的Java虛擬機等。

這些不同的虛擬機,都遵循相同的Java字節碼的規範。因此,只要把Java源代碼編譯成標準的Java字節碼,就可以在所有的這些虛擬機中運行。


對編譯器、OS內核、性能調優、虛擬化等技術感興趣的朋友,歡迎關注!


分享到:


相關文章: