技術沉澱 之C語言與組合語言的區別

2021-05-02 10:37:49 字數 3926 閱讀 6762

從事嵌入式系統開發多年,對於軟體方面,從初期的微控制器組合語言程式設計,到後來的c++介面程式編寫,已有相當多的經驗累積。正是有了多年的實戰經驗,對於彙編與高階語言在原理及應用等方面形成了自已的一些理解,也是我經常思考的問題,但一直沒有以書面的方式記錄下來,今天之所以寫下這些文字,正是想做乙個歸納,日後也好參考。  

其實,c語言與組合語言的區別一直是程式設計師們津津樂道的話題。如果你問乙個程式設計師這樣的問題,他也許會這麼回答你:「c語言可讀性好,**便於維護,便於開發;組合語言編寫的程式不容易看懂,可維護性不好,但是執行效率高。」這樣回答是沒有錯的,但只是乙個概括,不夠深入。比方說,組合語言為什麼執行效率比c語言高呢?c語言的可讀性又好在**呢?組合語言不同樣可以用註解來提高可讀性嗎?等等這些的問題。要真正能回答這些問題,不是一件簡單的事情,也不是三言兩語能解釋得清的,需要比較徹底地分析彙編與c的本質上的區別。  

先說彙編,寫過彙編的程式設計師都知道,「組合語言實質上機器語言的助記符。」這句話需要這樣來解析:1.cpu只能執行它所支援的指令集,而這些指令集當中的每天條指令都是一些二進位制數的序列,也就是「0」和「1」的有序組合;2.「0」和「1」的組合不便於程式設計師的記憶因此有了「mov a 0x40」等這樣的助記符,也就是說在程式設計師編寫程式的時候,用「mov a 0x40」來代替一串「0」和「1」的序列,這樣一看就知道是吧「0x40」單元中的資料搬到累加器a當中來。而如果是用0」和「1」的序列,毫無特徵,很難被程式設計師記住。這也是為什麼要有組合語言產生的原因了。  

以上對組合語言的解釋基本上就道出了組合語言的本質,知道了組合語言的本質,我們不難理解,組合語言編譯成cpu可執行的機器語言其實只要做乙個翻譯的動作就好了,因為,助記符與對應的二進位制指令是一一對應的。進而,我們再來解釋為什麼組合語言會比c語言有更高的執行效率。首先,我們要理解一點,類似於c的高階語言面對的物件是程式設計師,而不是cpu,為什麼這樣說呢?原因非常簡單,cpu不認識c語言,cpu只認識以「0」「1」形式存在的指令。而c語言的所有語法以及它**組織形式都是有助於程式設計師編寫**的。所以,c語言編寫完程式後,需要通過編譯器將c語言編譯成與相應cpu指令集對應的機器語言。問題來了,前面我們說過,組合語言與機器語言是一一對應的。但是c語言呢?當然沒這麼好事了。c語言的語法是固定的,c語言編寫的程式要編譯成cpu能讀懂的機器語言指令沒辦法一一對應,所以就需要有編譯規則了。比方說乙個for迴圈會有若干條實現對應for迴圈功能的機器指令對應,而乙個switch,也相應會有機器指令段代替。所以c語言最終要編譯機器**,必須要遵從許許多多的這樣的規則才行。我試驗過,用c編寫乙個簡單的程式,比方說只包含乙個for迴圈,編譯出的**和用彙編寫的最優**幾乎是一樣。但**量一大,由於受制於規則(不受制也不行呀,否則編不出來),編出來的**與用組合語言寫出來的**相比就走了不少「彎路」了。雖然說,現在的很多c編譯器在編譯的時候都會有優化,但是,不可能做得到效率上等同於與機器語言一一對應的組合語言的效率。畢竟,組合語言可以理解為直接就是面對cpu的,只不過是機器語言用助記符代替而矣。  

以上只是兩種語言效率上區別的乙個主要原因,其實,對於資源的利用上,組合語言同樣有優勢。彙編是直接面對cpu的語言,只要是在指令集支援的範圍內,組合語言可以直接而靈活地管理包括特殊功能暫存器、通用暫存器、儲存單元的每乙個位元組,甚至是每乙個bit。c語言對記憶體的使用及管理功能也是很強大的,但畢竟還是受制於語法。舉個最簡單的例子,c語言當中沒有對應三位元組或是五位元組的變數型別,要麼int型,要麼long型,所以每次申請必須是固定的位元組數,勢必造成記憶體使用上的浪費。而大部份組合語言根本沒有這樣的語法,在偽指令的幫助下(其實也只是提高可讀性),組合語言程式可以使用任意位元組數的變數,當然處理起來比c語言麻煩得多,最終還是乙個位元組乙個位元組地拼接處理,而用c語言寫程式就輕鬆了,不用管這些,最終編譯器會搞定嘛。而輕鬆的代價就是造成了浪費。而記憶體使用效率不高同時也會影響到整個程式的整體效率。  

彙編的最後部份,來說明一下偽批令這個東西吧。乙個不善於用偽指令寫匯程式設計序的程式設計師不是乙個好的程式設計師,這就和寫c語言不用巨集是乙個道理。偽批令存在價值在於他提高了組合語言的可讀性,同時也能簡化組合語言的程式設計。比方說最通用的建立立即數名稱,而不是用二進位制或十六進製制數;建立資料表;arm當中的建立全域性及區域性變數等。這個不多說了,針對於不同的mcu或cpu有不同的偽指令。  

再來說說c吧,c語言豐富而實用的語句決定了c語言程式靈活性以及強大的**組織能力。利用c語言,我們可以很方便地編寫出龐大的工程,在版本管理工具的幫助下,可以很輕鬆地實現多人協作程式設計。特別是引入rt-os以後,c語言的程式框架更加靈活了,新增功能(任務)更加輕鬆。因為,所有的任務的調控可以直接交給作業系統來做,而程式設計師需要做的是編寫任務(含乙個或多個功能模組)的內容,以及設定任務的優先順序,堆疊數等等。而任務間的通訊可以擺脫「全域性變數」這個禍害,完全可以通過訊號量、郵箱、佇列等形式來溝通。為什麼說「全域性變數」是禍害呢?單程式量不大的時候,「全域性變數」可能是好東西,因為方便嘛,哪都能改它,哪都能讀它。可是,一旦程式大了,原始碼檔案一多,如果都習慣用全域性變數來傳遞及存貯共用量的話,災難就會降臨。你會看到數以千計的全域性變數在各各函式間縱橫交錯,如果這些變數不是你建立的,你會很難知道它的作用,因為系統太大了,它出現的地方太多了,而且,像這樣的變數實在太多,你會因此而感到恐懼,相信很多有經驗的程式設計師都經歷過吧!然而,這將埋下系統崩潰級別的隱患。因為,這些全域性變數太多,而且出現在太多的地方,很難完全統計出哪些地方可能會修改它們,一量有遺漏,變數的值可能就會和我們想要的值有出入,後果非常嚴重。更有甚者,當全域性變數是指向陣列的指標或者是陣列本身的時候。有的程式設計師可能對多得數不清的這些變數感到困惑,容易犯的乙個錯誤是寫這些全域性陣列時沒有加以保護,經常都會寫出陣列的範圍,而將其它無關的變數給莫名其妙地改了。導致的後果可能是出錯,也有可能是宕機,而且,由於這種問題極其隱蔽,很難找出來。  

所以,在大的系統當中,幸虧有了os這種東西的存在,它不但能幫我們擺脫全域性變數這個禍害,而幫助程式設計師更加方便地組織各個功能模組。並且,讓每個任務單一化,進而降低了程式編寫的難度。而用組合語言編寫較大的工程,是困難重重的事情。首先,必須面對上面提到的全域性變數的問題,另外還得面對其它的困難,比方說記憶體的使用。在c語言裡,程式設計師只要申請各種型別的變數然後就可以使用了,而具體用的是哪個單元的空間,交給編譯器去管理就好了。而你用組合語言寫程式的時候,必須要指明所用記憶體的位址,問題來了,程式設計師不得不對所有記憶體的使用瞭如指掌,因為所有記憶體單元的使用都必須體現出來,這也是組合語言的特點。當程式量一旦大到某種呈度的時候,規劃這些記憶體的使用本身就是乙個高難度的工作,因為同時你還得保證各個地方在使用它們的時候沒有衝突。真的很難,我是有體會的。這時候,我們再想象一下,如果程式太大,我們要幾個人來協作編寫的話,問題就更加複雜了,因為,沒有了編譯器的幫忙,程式設計師之間要協商好記憶體使用的規則,這太難了,因為面對它們的只不過是一些數量龐大的位址空間,光是劃分區域倒是簡單,但涉及到程式間的互動很大的麻煩就來了,每個程式設計師必須提供各自的變數介面,因為彙編的可讀性本來就差,這些介面包括了每乙個可能共用的變數(在彙編中只是記憶體空間資源),以及說明它們的功能,這個工作量非常大,而且一旦做得不好,很容易出錯,出了錯還很難查。用彙編編寫程式還有很多比c困難的地方,再舉乙個簡單的例子。組合語言是低階語言,是機器語言面向程式設計師的乙個一對一的翻譯,所以對於程式設計師來說,它的功能不夠豐富。在c語言裡寫乙個(13200.68/98.56)*256.24的程式,可以直接就表示成"double a; a=(13200.68/98.56)*256.24;",而在彙編裡就沒這麼輕鬆了,彙編裡面一般都沒有直接支援浮點運算的指令,通常情況下都是得專門編寫乙個函式來做浮點運算。結果就是匯程式設計序編寫比c麻煩很多,而且還不直觀。另外,用組合語言編寫程式對程式設計師的要求也更高,因為,程式設計師必須能撐握cpu或mcu的記憶體結構、匯流排結構、功能模組、堆疊系統、中斷資源及機制等等,否則,是寫不下去的。  

最後總結,c語言與彙編根本不是一碼事,怎麼可能幾句話就能道出它們的區別呢?就目前的情形來說,由於ic工藝的成熟,mcu的存貯資源越來越便宜,工作頻率也越來越高,所以在資源利用率以及執行效率上沒有像以前要求那麼高了。而且,實現的功能越來越強大,這些因素都助長了c以及c++在嵌入式開發當中地位越來越高。就連mcs-51的程式編寫也以c語言主導了,這還要歸功於keil這個強大且十分容易入手的工具。面向程式設計師的高階語言比面向cpu的組合語言好用得,在硬體條件允許的情況下,程式設計師當然選擇用高階語言程式設計,不旦提高了程式設計效率,也提高了**的可維護性,並且十分有利於編寫大型的工程。

組合語言學習之組合語言源程式的輸入

在dos下輸入彙編源程式的方法 一 環境的搭建 二 熟悉debug的一些除錯指令 當顯示器顯示出提示符 時,說明已進入到debug狀態,此時,可以用debug命令列來操作 1.r 指令 用法 r 暫存器的名字 作用 用於檢視暫存器的值 register的首字母 或者修改暫存器的內容。當暫存器的名字省...

C語言轉成MIPS組合語言

功能 隨機輸入兩個整數,然後計算這兩個數的最小公倍數和最大公約數,並作為計算結果輸出。include stdio.h include conio.h int main a num1 b num2 while b 0 一直重複賦值和計算,直到找到正確結果 printf gongyueshu d n a...

c語言和組合語言的區別

什麼是c語言 c語言是一門通用 計算機程式語言,應用廣泛。c語言的設計目標是提供一種能以簡易的方式 編譯 處理低階 儲存器 產生少量的 機器碼以及不需要任何執行環境支援便能執行的程式語言 儘管c語言提供了許多低階處理的功能,但仍然保持著良好跨平台的特性,以乙個標準規格寫出的c語言程式可在許多電腦平台...