什麼指令編譯後生成機器指令
① 我們所寫的每條c語句,經過編譯最終都轉化成二進制的機器指令為什麼對
c語言中的注釋是不會轉化為二進制的。
第一句話,注意「每條c語句」,c語句是不包含注釋,所以正確。
第二句話,「每條可執行語句和非執行語句」,顯然注釋語句也被包含在其中,所以錯誤。
這種題就是玩了個文字游戲
② 用高級語言編寫的源程序,經編譯後產生的是
目標程序。
目標程序,又稱為「目的程序」,為源程序經編譯可直接被計算機運行的機如岩伏器碼集合,在計算機文件上以.obj作擴展名----由語言處理程序(匯編程序,編譯程序,解釋程序)將源程序處理(匯編,編譯,解釋)成與之等價的由機器碼構成的,計算機能夠直接運行的程序,該程序叫目標程序。
目標代碼盡管已經是機渣攜器指令,但是還不能運行,因為目標程序還沒有解決函數調用問題,需要將各個目標程序與庫函數連接,才能形成完整的可執行程序。
(2)什麼指令編譯後生成機器指令擴展閱讀
計算機並不能直接地接受和執行用高級語言編寫的源程序,源程序在輸入計算機時,通過"翻譯程序"翻譯成機器語言形式的目標程序,計算機才能識別和執行。這種"翻譯"通常有兩種方式,即編譯方式和解釋方式。
編譯方式是指利用事先編好的一個稱為編譯程序的機器語言程序,作為系統軟體存放在計算機內,當用戶將高級語言編寫的源程序輸入計算機後,編譯程序便把源程序整個地翻譯成用機器棗扮語言表示的與之等價的目標程序。
然後計算機再執行該目標程序,以完成源程序要處理的運算並取得結果。解釋方式是指源程序進入計算機後,解釋程序邊掃描邊解釋,逐句輸入逐句翻譯。
計算機一句句執行,並不產生目標程序。如PASCAL、FORTRAN、COBOL等高級語言執行編譯方式;BASIC語言則以執行解釋方式為主;而PASCAL、C語言是能書寫編譯程序的高級程序設計語言。
③ c語言編譯預處理
編譯,編譯程序讀取源程序(字元流),對之進行詞法和語法的分析,將高級語言指令轉換為功能等效的匯編代碼,再由匯編程序轉換為機器語言,並且按照操作系統對可執行文件格式的要求鏈接生成可執行程序。
如果用一張圖來表示:
讀取c源程序,對其中的偽指令(以#開頭的指令)和特殊符號進行處理
[析] 偽指令主要包括以下四個方面
(1)宏定義指令,如#define Name TokenString,#undef等。對於前一個偽指令,預編譯所要做的是將程序中的所有Name用TokenString替換,但作為字元串常量的Name則不被替換。對於後者,則將取消對某個宏的定義,使以後該串的'出現不再被替換。
(2)條件編譯指令,如#ifdef,#ifndef,#else,#elif,#endif,等等。這些偽指令的引入使得程序員可以通過定義不同的宏來決定編譯程序對哪些代碼進行處理。預編譯程序將根據有關的文件,將那些不必要的代碼過濾掉
(3)頭文件包含指令,如#include "FileName"或者#include 等。在頭文件中一般用偽指令#define定義了大量的宏(最常見的是字元常量),同時包含有各種外部符號的聲明。採用頭文件的目的主要是為了使某些定義可以供多個不同的C源程序使用。因為在需要用到這些定義的C源程序中,只需加上一條#include語句即可,而不必再在此文件中將這些定義重復一遍。預編譯程序將把頭文件中的定義統統都加入到它所產生的輸出文件中,以供編譯程序對之進行處理。
包含到c源程序中的頭文件可以是系統提供的,這些頭文件一般被放在/usr/include目錄下。在程序中#include它們要使用尖括弧(<>)。另外開發人員也可以定義自己的頭文件,這些文件一般與c源程序放在同一目錄下,此時在#include中要用雙引號("")。
(4)特殊符號,預編譯程序可以識別一些特殊的符號。例如在源程序中出現的LINE標識將被解釋為當前行號(十進制數),FILE則被解釋為當前被編譯的C源程序的名稱。預編譯程序對於在源程序中出現的這些串將用合適的值進行替換。
注意:
預編譯程序所完成的基本上是對源程序的「替代」工作。經過此種替代,生成一個沒有宏定義、沒有條件編譯指令、沒有特殊符號的輸出文件。這個文件的含義同沒有經過預處理的源文件是相同的,但內容有所不同。下一步,此輸出文件將作為編譯程序的輸出而被翻譯成為機器指令。
④ C語言代碼組成 - BSS、Data、Stack、Heap、Code、Const
一段C語言經過編譯連接後,成為一段可以運行的代碼,可運行的代碼可以分為以下四個部分組成:全局變數/靜態變數區、堆、棧、代碼區。其中全局變數/靜態變數區又分為未初始化變數區和初始化變數區,代碼區又分為代碼和常量區。即匯總下來,代碼可以分為6部分組成,包括:BSS區(未初始化的全局變數/靜態變數區)、Data區(實始化的全局變數區)、Stack區(棧區)、heap區(堆區)、Code區(代碼區)、const區(常量區)。
一、BSS區和Data區
C語言編程中定義的全局變數、靜態局部變數,就是分配在全局變數/靜態變數區域,但是為什麼又要分為BSS區域和Data區域呢?其實我們在定義全局或者靜態變數區,有時我會對它賦初始值,有的又不會賦初始化,比如我們定義的全局變數,初始化的賦值,是怎麼樣寫到變數區域中的,我們定義的靜態局部變數,在定義時初始化後,為什麼後面函數被調用,又不會再初始化呢?這個局部靜態變數是怎麼樣實始化的,什麼時候初始化的?
如果分析編譯後的匯編代碼,就會發現在代碼運行起來後,會有一段給變數賦值的指令,這一段代碼,不是我們C代碼對應的匯編,而是C編譯器生成的匯編譯代碼,這段代碼的作用就是給初始化了的靜態變數和全局變數進行初始化。這也是為什麼全局/靜態變數區域,要分BSS和Data的原因。
二、Stack區
棧是一種先進後出的數據結構,這種數據結構正好完美的匹配函數調用時的模型過程,比如函數f(a)在運行過程中調用函數f(b),f(a)在運行過程中的變數就是分配在棧中,通過在調用f(b)前,會將代碼中用到的R0~Rn寄存器的值保存到棧中,同時將函數的傳入參數寫入到棧中,然後進入f(b)函數,函數f(b)的變數b分配在棧中,當函數運行完畢後,釋放變數b,將棧中存放的f(a)函數的運行的R0~Rn寄存器值恢復到寄存器中,同時f(b)的返回結果存入到棧中,這樣f(a)繼續運行。當一個函數運行完畢後,它在棧中分配的臨時變數會全部釋放。
對於中斷也是一樣的,中斷發生時,也是一個函數打斷了另一個函數的運行,這種現場的保存(即寄存器的值),都是通過棧來完成的。所以棧的作用有:
三、Heap區
全局變數分配的內存在代碼整個運行周期內都是有效的,而在棧區分配的內存在函數調用完成後,就會釋放。這兩種內存模型都是由編譯器決定它的使用,代碼是無法控制的。那有沒有內存是由用戶控制的,要用時,就自由分配,不用時,就自行釋放?答案是肯定的,這部分內存就是堆。
用戶需要使用的動態內存,就是通過malloc函數,調用分配的,在沒有釋放前,可一直由代碼使用。當這部分內存不再需要使用時,可以通過free函數進行釋放,將它歸還到堆中。從這中可以看出,堆的內存,是按需分配的。這就是賦予了代碼很大的自由度,但這也是會帶來負作用的,比如:內存碎片化導致的malloc失敗;忘記釋放內存導致的內存泄露,而這些往往是致命的失誤。
四、Code區
代碼區就是編譯後機器指令,這些指令決定了功能的執行。我們編譯的代碼一般是下載進flash中,但是運行,卻有兩種方式:在RAM中運行和在ROM中運行。 在RAM中運行,即是boot啟動後,將flash中的代碼復制到RAM中,然後PC指針在指到RAM中的代碼中開始運行。 有時在調試時,我們可以直接將代碼下載進RAM中運行進行調試,這樣加快調試速度。便是大部分的情況我們的代碼是從flash中開始運行的。
五、常量區
代碼中的常量,一部分是作為立即數,在代碼區中,但是像定義的字元串、給某數組賦值的一串數值,這些常量,就存在常量區,我們常用const來定義一個常量,即該變數不能再必變。這部分的變數,編譯器一般將它定義的flash中。
六、各個區域大小的是如何決定的:
code區和const區:是由代碼的大小和代碼中常量的多少來決定的。
bss區和data區:這是由代碼中定義的全局變數和局部變數的多少來決定的。
stack區:這個可以由使用都自行定義大小,但使用都要根據自已代碼的情況,評估出一個合理的值,再定義其大小,如果定義的太小,很容易爆棧,導至代碼異常,但是如果定義的太大,就容易浪費內存。
heap區:RAM剩下的部分,編譯器就會作為堆區使用。
七、嵌入式代碼一般啟動過程
以STM32為例,通過分析其匯編啟支代碼,大致可以分為以下幾個步驟:
如果大家想看編譯扣,代碼文件的組成,可以查看統後生的map文件,裡面有詳細的數據,包括各個函數的分配內存,BSS,Data,Stack,Heap,Text的分配情況。
如果相要了解詳細的代碼啟動過程,可看它的啟動匯編文件。
⑤ java代碼到底是如何編譯成機器指令的
編譯器把一種語言規范轉化為另一種語言規范的這個過程需要哪些步驟?回答這個問題需要參照《編譯原理》,總結過程如下:
1)詞法分析:讀取源代碼,一個位元組一個位元組的讀進來,找出這些詞法中我們定義的語言關鍵詞如:if、else、while等,識別哪些if是合法的哪些是不合法的。這個步驟就是詞法分析過程。
詞法分析的結果:就是從源代碼中找出了一些規范化的token流,就像人類語言中,給你一句話你要分辨出哪些是一個詞語,哪些是標點符號,哪些是動詞,哪些是名詞。
2)語法分析:就是對詞法分析中得到的token流進行語法分析,這一步就是檢查這些關鍵片語合在一起是不是符合Java語言規范。如if的後面是不是緊跟著一個布爾型判斷表達式。
語法分析的結果:就是形成一個符合Java語言規定的抽象語法樹,抽象語法樹是一個結構化的語法表達形式,它的作用是把語言的主要詞法用一個結構化的形式組織在一起。這棵語法樹可以被後面按照新的規則再重新組織。
3)語義分析:語法分析完成之後也就不存在語法問題了,語義分析的主要工作就是把一些難懂的,復雜的語法轉化成更簡單的語法。就如難懂的文言文轉化為大家都懂的百話文,或者是注釋一下一些不懂的成語。
語義分析結果:就是將復雜的語法轉化為簡單的語法,對應到Java就是將foreach轉化為for循環,還有一些注釋等。最後生成一棵抽象的語法樹,這棵語法樹也就更接近目標語言的語法規則。
4)位元組碼生成:將會根據經過注釋的抽象語法樹生成位元組碼,也就是將一個數據結構轉化為另外一個數據結構。就像將所有的中文詞語翻譯成英文單詞後按照英文語法組裝文英文語句。代碼生成器的結果就是生成符合java虛擬機規范的位元組碼。