編譯原理實現tcc
『壹』 編譯原理與什麼課程有聯系
與人工智慧、機器學習、操作系統、編譯器等都有關系,屬於計算機專業的核心課程,需要扎實掌握;
『貳』 用c語言編寫的程序怎麼把它做成可以運用的軟體
1、建立一個可執行過程是先把源程序編譯成目標文件(帶.OBJ擴展名的機器代碼文件),然後再把目標文件通過連接程序轉換成可執行文件(帶.EXE擴展名)。
2、執行過程不同的開發環境有的不同,這里以TC2.0為例說明:
a、單個文件源代碼的編譯:
假設文件名為:hello.c,文件內容為:main(){printf("Hello World.");}
1)用TC2.0集成環境生成可執行文件:
在TC集成環境IDE中打開源代碼文件hello.c,然後按F9,即可生成可執行文件hello.exe。
2)從命令行直接編譯、連接,生成可執行文件:
在命令行執行tcc可以編譯c語言源文件,並把它連接成可執行文件。
格式:tcc [選擇項 選擇項 選擇項 ...] 文件名 文件名,如:tcc hello
b、多個文件源代碼的編譯:
1)先後輸入並編輯4個文件,並以file1.c、file2.c、file3.c、file4.c存儲在磁碟上。
2)在編譯狀態下,建立一個「項目文件」,它不包括任何程序語句,而只包括組成程序的所有文件的文件名 file3 file1 file2 file(擴展名可以省略)
3)將以上內容存檔,文件名自定,但擴展名必須為.prj(表示為工程文件)。
4)在TC環境中的Project菜單找到Project name,打開剛存的.prj文件。
5)按F9進行編譯連接。
6)按Ctrl+F9,運行可生成的可執行程序。
c、多文件也可以在命令行單個編譯,然後連接。
『叄』 編譯器由什麼組成
編譯器:翻譯工具,把高級語言源程序翻譯為匯編語言源程序,再把匯編源序翻譯成目標代碼供連接程序使用。
語言:一個規則。例如:C語言,它規定程序入口為main(),和其它規定。並把這些 <規定的集合> 命名為C語言。
TC:一個開發工具,它包括文本編輯器,編譯器,連接程序,調試環境等等。TC中的編譯器是TCC.EXE文件。
關於匯編:
匯編語言是一個規則,
匯編程序是一個翻譯工具
匯編源程序是一個 符合(匯編語言)規則的程序代碼
匯編是一個過程,這個過程是匯編程序 把匯編源程序 翻譯 為目標代碼的過程
對於高級語言:翻譯的過程叫做編譯,翻譯工具叫做編譯程序或編譯器。
『肆』 如何C語言與匯編混編
c語言可以嵌套匯編:
按照TC2.0的幫助系統所以說的,在TC2.0下是可以用匯編的,方法是使用asm關鍵字:其格式是:
asm opcode <operands> <;newline>,如同別的注釋一樣,<>之間的表示可選的;例如:
main()
{
char *c="hello,world/n/r$";
asm mov ah,9;asm mov dx,c;asm int 33;
printf("You sucessed!/n");
}
或者是:
main()
{
char *c="hello,world/n/r$";
asm mov ah,9
asm mov dx,c
asm int 33
printf("You sucessed!");
}
兩種格式其實是一種.如果你用的是第一種的樣式,記住:
每一句匯編語句都要以asm開頭,如果一行內有多個句子,
那麼千萬不要忘記在兩個句子之間的這個semicolon(分號),
但是最後一句匯編後面(如果後面沒有其它的語句)的分號可有可無,象第一個例子中的
asm int 33;後面的分號就可以不要,因為它的後面沒有其它
的語句了.但如果是這樣:
asm mov ah,9; asm mov dx,c;asm int 33; printf("You sucessed!");
那麼asm int 33;後面的分號便還是留下好,以免出現編譯錯誤!
在這一點上頗象C語言.
還有一種格式是
asm{ assembly language statement},這種格式應該被普遍的歡迎.
它們的例子如下(其中的語句排列格式與上面兩種相同):
asm{
mov ax,var1
add ax,var2
......
}
但是要注意這種格式TC2.0是不支持的!
只有後來的TC++3.0及後來的IDE支持!
工具的使用:
一旦你的C源文件里包括了這些好東西,則必須用TCC.EXE的COMMAND-LINE來編譯,具體的命令參數TCC.EXE已經提供,這里不復闡述了.最簡單的是:TCC C源文件名(使用這個方法,TCC會自動調用TASM.EXE和TLINK.EXE,並且能夠使TLINK.EXE正確的找到需要的.obj和.lib文件,如果你單步編譯的話,可能會碰到很多的問題,主要是TLINK.EXE它自己並不會去找.obj和.lib文件,你自己可以建一個.bat文件,如果要指定.lib文件的目錄的話可以用/L參數,在文章的後面有一個例子).但大家要注意了,看一下你的TC目錄下面到底是否有TASM.EXE文件,並在TURBOC.CFG(這個文件包括TCC.EXE運行期參數,這裡面所有參數在運很期都將被自動TCC.EXE使用,例如:-IH:/TC/INCLUDE/
-LH:/TC/LIB/)文件中設置好一些參數,並確認TASM.EXE的版本號要2.0以上,以及是否能夠向下兼容.但是在大多數的情況下TC的目錄是沒有TASM.EXE的,或是版本不正常.
如果你有TASM.EXE文件並且TURBOC.CFG文件也已經寫好了,但是還要注意一個
問題:運行TCC.EXE時要在獨立的DOS SHELL下面(不要害怕,這不是一個新東西,我的意思
是,不在諸如TC下的DOS SHELL下面運行,我曾經敗在這個問題下,當我發現時直想揍電腦
一頓,還好沒有,不然就沒有這篇文件了.)
還有一句重要的話:TC2.0支持大部分8086指令(當然用法有一些約定,不過現在我並不打算
進行詳細說明,因為那是一件很繁雜的事,以後有時間或許會寫出來----如果大家需要的話).
如果說上面我所說的那些約定很繁雜的話,那麼下面的方法該是多麼簡單啊!
讓我們使用Borland為TC2.0內建的變數來進行偽匯編.
或許你還不知道在TC2.0中還有一些內建的pseudo寄存器(可以看作是register 型的變數,但是它們比register型的變數好用的多)
_AX,_AH,_AL,
_BX,_BH,_BL,
_CX,_CH,_CL,
_DX,_DH,_DL,
_DI,_SI,_SP,
_CS,_DS,_ES,_SS
注意這些寄存器的size,_AX,_BX,_CX,_DX,_CS,_DS,_ES,_SS,_SI,_DI,_SP等都是16位的寄存器相當於C語言的unsigned int類型,其餘的都是8位的寄存器(相當於unsigned char)(TC怎麼可能支持32位的寄存呢,所以EAX等是不能用的,FS,GS和IP寄存器都是無效的),還有就是在傳遞參數的時候千萬不要忘記使用強制類型轉換.
中斷調用指令是:__int__(interrupt_#)(注意int的前輟和後輟都是兩個underscores)
For example:
#include<dos.h>
unsigned int _stklen=0x200;
unsigned int _heaplen=0;
main()
{
_DX=(unsigned int)"Hello,world./r/n$";
_AX=0x900;
__int__(0x21);
}
dos.h它是包含__int__()內建中斷調用語句的頭文件,因此是不可
缺少的._stklen和_heaplen是定義運行期堆棧和堆大小的兩個內部
引用變數(這是個我自己想的名詞,意指如果這兩個變數在源文件中
顯式的聲明了,那麼編譯程序會自會引用來構造編譯時期的信息以產生
用戶希望的目標文件,如果不顯式的聲明則編譯程序自動確定).
這兩個變數也有一些約定,如果_stklen不顯式聲明,_heaplen賦值為零
都表示棧和堆都是defult的.
最後在TC2.0中還有一個沒有說明的標志位寄存器flags,它也是內建
pseudo寄存器是:_FLAGS,是一個16位寄存器.這些內建的寄存器都可以進行
運算,但是要注意它們所代表的類型(必要時進行類型轉換);
看起來這是不是一種好的辦法啊(而且使用這種方法只要用個一個dos.h頭文件就好,
不需要用TCC編譯,可以直接在TC20的IDE下編譯).
TC2.0中也提供了一些簡單好用的函數來實現對DOS功能的調用如:
int86(...),int86x(...)(但是這些方法實際仍然要調用函數,所以不如使用
偽寄存器,又因為要牽涉到union REGS結構的內存分配所以系統的開銷是增大了,
而使用偽寄存器是最簡潔的),埠通信函數如:inportb(...),inport(...),
outportb(...),outport(...),指針轉換函數:FP_OFF,FP_SEG,MK_FP,這些函數在
幫助系統中都有,有用時大家可以查閱.
tlinkbat.bat的例子:
rem The lib environment variable is the directory of the .obj and .lib file
set lib=h:/tc/lib/
rem 這下面的句子中的c0s(C 零S)是一個.OBJ文件,是一個C程序的STARTUP文件
tlink %lib%c0s %1,%1,%1,/L%lib%emu.lib %lib%maths.lib %lib%cs.lib
set lib=
(使用時可將以rem開頭的句子刪除)
___________________________________________________
一些約定:
我們先說一下在TC20下寫匯編(內聯匯編--自己起的名字,大家可以想叫什麼叫什麼)時的編譯器的編譯原則:
1.所有在main()函數外的的匯編語言的語句都作為數據聲明語句處理,也即在編譯器編譯時會將它放在數據段中,如:
asm string1 db "Hello",,,'world!',0ah,0xd,"$"
main()
{
asm mov dx,offset string1
asm mov ah,9
asm int 33
asm mov dx,offset string2
asm int 33
}
asm string2 db "the string can be declared after the main() function!$"
象這些樣子在main()外面的匯編語言的數據定義語句(事實上不管是什麼匯編語句,
只要是在main()之外,包括這個句子:asm mov ax,0x4c00),在編譯後都放在數據段中,而C語言的數據聲明語句仍按C的規則!
2.所有在main()函內的匯編語言的語句在編譯後都放在代碼段中,包括這個句子:
asm string2 db "the string can be declared after the main() function!$"
3.不要在以asm 開頭的語句中使用C語言的關鍵字,這會導致編譯階段的錯誤
那麼,根據這三條大家會得到什麼樣的結論呢?(先閉上眼想一想,你可能會由此變的
很贊賞自己,是的你應該這樣相信自己是對的!)
讓我們一起看一下這個結論:
1.根據編譯原則1得到:不可以在main()外面寫匯編命令語句(不要笑,正是與C語言相同才值得注意!),在任何地方都不要進行任何的段定義和宏定義(這是因為編譯後的形式決定的,也即:在TC20下所有的匯編格式的語句只能是,直接性的數據定義和語句指令)!
2根據編譯原則2得到:不可以在main()之內使用匯編的語句進行數據定義(同樣不要笑,
大多數人在第一次在TC20下寫匯編都會有這樣的錯誤的)
3.如同類強制類型這樣的事是不可以在以asm開頭的匯編語句中使用的
好了,天即朗,氣瞬清!這樣一說,一個大體的框架就出來了!只要遵守這個原則寫,就可避免很多莫名其妙的錯誤出現!
通俗的說:
匯編語句的數據定義放在main()外面,指令放在main()裡面.
如果你沒有更好的文檔,那麼記住我的這些話!
一些細節的問題:
在以asm開頭的內聯匯編語句中是不支持C的轉義字元的,但是用C語言聲明一個字元數組(含有轉義字元的),然後用int 33 ah=9這功能時輸出這個字元串時,其中的轉義字元是有效的(這主要是因為編譯後其內部表示形式不同造成的,自己想想會有答案的).
內聯匯編支持C的一些如數值表示,字元串聲明格式等,
如:一個十六進制的數據可以用兩種方式表示:0xa 和0ah,字元串可以是這樣:
"Hello,world!$"(如同C)也可以這樣'Hello,world!$'(用匯編自己的方式).
象C一樣你同樣要注意賦值的類型,而且要比C更嚴格(匯編從來不自己動手做
如同類型轉換啊這樣事),所以一切的事完全要你自己做好!而且你不要企圖以C的形式
做這件事,如這樣的格式 asm mov dx,(unsigned)a(a是一個這樣的東西,
char a[ ]="hello,world!";),而且這樣句子也會導致錯誤:asm mov dx,word ptr a(邏輯錯誤),不過這不是在編譯時的錯誤,而是運行期的錯誤(具體的原因自己想一想,象word label這樣的東西的運算作用和會導致的後果),你可以這樣用一個句子做"中間人"如int i=(unsigned)a;asm mov dx,i(也千萬不要用asm mov dx,(unsigned)a 這樣的句子.但是,告訴大家一個好消息,你可以用指針指向一個字元串,然後你會驚訝你竟然可以這樣:
char *p="hello,world";asm mov dx,p,然後用int 33 ah=9的功能輸出這個字元串而不會有錯誤(這也表現出指針的特點,它是一個二位元組的(TC20下)變數,含有的是一個地址,這與其指向的變數的類型是毫無關系的).
內匯匯編語句不支持->這個運算符.還有標號的問題,在最後的例子中你會年看到一些特別之處!
上面所說的只是很細小並微少的一些事(也是很常遇到的),尚有很多的細節要說,但由於本人時間有限不能一一列舉,如C的結構在內聯匯編的應用等大家可以按照其運行機理去想想一下用法;另外,由於這只是一件學習的事,所以還是大家自己學(找一下有關文檔,當然現在已經沒有什麼比較完整的了),情況會好的多,我在對內聯匯編的學習過程中領會到了不少的東西,例如編譯原理方面的知識,以及如何做會使代碼更高效,占空間最少等的方法.最後向大家推薦一種方法,在利用TCC的-S開關可以生成C源文件的匯編代碼
(或許很多的人都用過)是很好的學習材料!祝大家學有所成!
Cstarter
02-11-17
/* 由於個人的時間和能力有限,難免有錯誤和不詳細的地方,請大家見諒!
My Email:[email protected] [email protected] QQ:170594633 */
一些例子:
下面這個例子是對沈美明 溫冬嬋的
<<IBM-PC 匯編語言程序設計>>清華版第十一章程序的改寫
可直接在命令行上鍵入 tcc filename 就可以,當然你要有TASM.EXE
/*
asm mus_frep dw 330,294,262,294,3 p(330)
asm dw 3 p(294),330,392,392
asm dw 330,294,262,294,4 p(330)
asm dw 294,294,330,294,262,-1
asm mus_time dw 6 p(25),50
asm dw 2 p (25,25,50)
asm dw 12 p(25),100
*/
asm mus_frep dw 330,392,330,294,330,392,330,294,330
asm dw 330,392,330,294,262,294,330,392,294
asm dw 262,262,220,196,196,220,262,294,330,262
asm dw -1
asm mus_time dw 3 p (50),25,25,50,25,25,100
asm dw 2 p (50,50,25,25),100
asm dw 3 p (50,25,25),100
main()
{
asm jmp start
/*設置發聲的頻率,這一段在沈美明 溫冬嬋的
<<IBM-PC 匯編語言程序設計>>清華版第十一章有詳細的說明 */
sound:
asm mov al,0b6h
asm out 43h,al
asm mov dx,12h
asm mov ax,533h*896
asm div di
asm out 42h, al
asm mov al,ah
/* 這個延時是用來防止兩次IO操作的最後一次操作的錯誤,
因為CPU比匯流排的速度快很多,所以 要延時等待第一次操作完成後再進行第二次操作*/
asm mov cx,1000
delay:
asm loop delay
asm out 42h,al
asm in al,61h
asm mov ah,al
asm or al,3
asm out 61h,al
/* 使用中斷15H功能86H延時CX:DX=微秒數*/
asm mov ax,2710h
asm mul bx
asm mov cx,dx
asm mov dx,ax
asm mov ah,86h
asm int 15h /*可用__int__(0x15);代替*/
asm mov al,ah
asm out 61h,al
asm jmp add_count
/*------------------*/
start:
asm mov si,offset mus_frep
asm lea bp,mus_time
frep:
asm mov di,[si]
asm cmp di,-1
asm je end_mus
asm mov bx,[bp]
asm jmp sound
add_count: /*標號不能用匯編語言寫*/
asm add si,2
asm add bp,2
asm jmp frep
end_mus:;
}
對於上面的程序大家可用偽寄存器的方法寫一個,要容易的多!
/*一個發聲程序!(引自<<PC技術內幕>>電力版--這個版不好,不如清華版的)*/
#include"dos.h"
main()
{
static union REGS ourregs;
outportb(0x43,0xb6);
outportb(0x42,0xee);
outportb(0x42,0);
outportb(0x61,(inportb(0x61)|0x03));
ourregs.h.ah=0x86;
ourregs.x.cx=0x001e;
ourregs.x.dx=0x8480;
int86(0x15,&ourregs,&ourregs);
outportb(0x61,(inportb(0x61)&0xfc));
}
『伍』 C語言編譯產生的.OBJ文件和匯編編譯產生的.OBJ文件,如何連接
一同學習。。。
Trubo C的命令行編譯連接
所謂命令行編譯,是指在dos下,調用Trubo C的tcc.exe程序.來完成對turbo C源程序的編譯連接工作.當選擇對後綴為*.asm的匯編程序文件編譯時,tcc還要調用TASM後才能對後綴為.asm的文件進行編譯,這種方式適合於c程序與匯編語言混合編程的編譯連接,當c程序嵌入匯編指令時,也必須用此方法編譯連接。
命令行編譯的格式為:
tcc [選項1 選項2 ...]文件名1 文件名2...其中選項是指對後面給出的文件進行連接時的選擇項,可選的常用選擇項如下所示:每個選項前都帶有"-"號,且大小寫是區分的。文件名是指源文件.c或目標文件.obj或庫文件.lib當不指定只編譯不連接時,tcc將完成編譯和連接兩個步驟,對.lib庫只進行形式上的連接,標准庫用戶不用進行連接。
例如:
tcc -ib:\include -lb:\lib -etest start.c body.obj myc當執行該命令時,表示將start.c源文件和body.obj目標文件及myc.c(命令行中該文件無後綴),分別進行編譯(對body.obj文件不再編譯),然後連接生成名為test的執行文件test.exe(由-test給出).
-ib:\include 表示包含文件的路徑是b:\include
-ib:\lib 表示庫文件的路徑是b:\lib
又例如:
tcc -ms -efile -lc:\tc\lib file1 file2.obj graphics.lib
其中-ms表示選擇小內存模式進行編譯,它也是turbo c的預設編譯模式,將file1進行編譯,然後和file2.obj 及graphics.lib進行連接.生成file.exe的可執行文件.其中graphcis.lib庫的路徑為c:\tc\lib,即意為在c:\tc\lib目錄下去尋找graphics.lib文件.當進行混合編程時,如果已有匯編程序s3.asm其命令行可寫為
tcc ic:\tc\include -lc:\tc\lib -mm s1 s2 s3.asm mylib.lib
表示用中模式(-mm)編譯源文件s1.c和s2.c,調用TASM對s3.asm進行編譯,然後連接生成可執行文件s1.exe,編譯時,到c:\tc\include目錄中去找包含文件,到
c:\tc\lib目錄中去找庫文件mylib.lib.
---------------------------------------
http://topic.csdn.net/t/20010308/22/81569.html
http://www.down22.org/plus/view.php?aid=15882
比VC難用。。。
『陸』 編譯原理
編譯原理是計算機專業的一門重要專業課,旨在介紹編譯程序構造的一般原理和基本方法。內容包括語言和文法、詞法分析、語法分析、語法制導翻譯、中間代碼生成、存儲管理、代碼優化和目標代碼生成。 編譯原理是計算機專業設置的一門重要的專業課程。編譯原理課程是計算機相關專業學生的必修課程和高等學校培養計算機專業人才的基礎及核心課程,同時也是計算機專業課程中最難及最挑戰學習能力的課程之一。編譯原理課程內容主要是原理性質,高度抽象[1]。
中文名
編譯原理[1]
外文名
Compilers: Principles, Techniques, and Tools[1]
領域
計算機專業的一門重要專業課[1]
快速
導航
編譯器
編譯原理課程
編譯技術的發展
編譯的基本流程
編譯過程概述
基本概念
編譯原理即是對高級程序語言進行翻譯的一門科學技術, 我們都知道計算機程序由程序語言編寫而成, 在早期計算機程序語言發展較為緩慢, 因為計算機存儲的數據和執行的程序都是由0、1代碼組合而成的, 那麼在早期程序員編寫計算機程序時必須十分了解計算機的底層指令代碼通過將這些微程序指令組合排列從而完成一個特定功能的程序, 這就對程序員的要求非常高了。人們一直在研究如何如何高效的開發計算機程序, 使編程的門檻降低。[2]
編譯器
C語言編譯器是一種現代化的設備, 其需要藉助計算機編譯程序, C語言編譯器的設計是一項專業性比較強的工作, 設計人員需要考慮計算機程序繁瑣的設計流程, 還要考慮計算機用戶的需求。計算機的種類在不斷增加, 所以, 在對C語言編譯器進行設計時, 一定要增加其適用性。C語言具有較強的處理能力, 其屬於結構化語言, 而且在計算機系統維護中應用比較多, C語言具有高效率的優點, 在其不同類型的計算機中應用比較多。[3]
C語言編譯器前端設計
編譯過程一般是在計算機系統中實現的, 是將源代碼轉化為計算機通用語言的過程。編譯器中包含入口點的地址、名稱以及機器代碼。編譯器是計算機程序中應用比較多的工具, 在對編譯器進行前端設計時, 一定要充分考慮影響因素, 還要對詞法、語法、語義進行分析。[3]
1 詞法分析[3]
詞法分析是編譯器前端設計的基礎階段, 在這一階段, 編譯器會根據設定的語法規則, 對源程序進行標記, 在標記的過程中, 每一處記號都代表著一類單詞, 在做記號的過程中, 主要有標識符、關鍵字、特殊符號等類型, 編譯器中包含詞法分析器、輸入源程序、輸出識別記號符, 利用這些功能可以將字型大小轉化為熟悉的單詞。[3]
2 語法分析[3]
語法分析是指利用設定的語法規則, 對記號中的結構進行標識, 這包括句子、短語等方式, 在標識的過程中, 可以形成特殊的結構語法樹。語法分析對編譯器功能的發揮有著重要影響, 在設計的過程中, 一定要保證標識的准確性。[3]
3 語義分析[3]
語義分析也需要藉助語法規則, 在對語法單元的靜態語義進行檢查時, 要保證語法規則設定的准確性。在對詞法或者語法進行轉化時, 一定要保證語法結構設置的合法性。在對語法、詞法進行檢查時, 語法結構設定不合理, 則會出現編譯錯誤的問題。前端設計對精確性要求比較好, 設計人員能夠要做好校對工作, 這會影響到編譯的准確性, 如果前端設計存在失誤, 則會影響C語言編譯的效果。[3]
『柒』 編譯原理
C語言編譯過程詳解
C語言的編譯鏈接過程是要把我們編寫的一個C程序(源代碼)轉換成可以在硬體上運行的程序(可執行代碼),需要進行編譯和鏈接。編譯就是把文本形式源代碼翻譯為機器語言形式的目標文件的過程。鏈接是把目標文件、操作系統的啟動代碼和用到的庫文件進行組織形成最終生成可執行代碼的過程。過程圖解如下:
從圖上可以看到,整個代碼的編譯過程分為編譯和鏈接兩個過程,編譯對應圖中的大括弧括起的部分,其餘則為鏈接過程。
一、編譯過程
編譯過程又可以分成兩個階段:編譯和匯編。
1、編譯
編譯是讀取源程序(字元流),對之進行詞法和語法的分析,將高級語言指令轉換為功能等效的匯編代碼,源文件的編譯過程包含兩個主要階段:
第一個階段是預處理階段,在正式的編譯階段之前進行。預處理階段將根據已放置在文件中的預處理指令來修改源文件的內容。如#include指令就是一個預處理指令,它把頭文件的內容添加到.cpp文件中。這個在編譯之前修改源文件的方式提供了很大的靈活性,以適應不同的計算機和操作系統環境的限制。一個環境需要的代碼跟另一個環境所需的代碼可能有所不同,因為可用的硬體或操作系統是不同的。在許多情況下,可以把用於不同環境的代碼放在同一個文件中,再在預處理階段修改代碼,使之適應當前的環境。
主要是以下幾方面的處理:
(1)宏定義指令,如 #define a b。
對於這種偽指令,預編譯所要做的是將程序中的所有a用b替換,但作為字元串常量的 a則不被替換。還有 #undef,則將取消對某個宏的定義,使以後該串的出現不再被替換。
(2)條件編譯指令,如#ifdef,#ifndef,#else,#elif,#endif等。
這些偽指令的引入使得程序員可以通過定義不同的宏來決定編譯程序對哪些代碼進行處理。預編譯程序將根據有關的文件,將那些不必要的代碼過濾掉
(3) 頭文件包含指令,如#include "FileName"或者#include <FileName>等。
在頭文件中一般用偽指令#define定義了大量的宏(最常見的是字元常量),同時包含有各種外部符號的聲明。採用頭文件的目的主要是為了使某些定義可以供多個不同的C源程序使用。因為在需要用到這些定義的C源程序中,只需加上一條#include語句即可,而不必再在此文件中將這些定義重復一遍。預編譯程序將把頭文件中的定義統統都加入到它所產生的輸出文件中,以供編譯程序對之進行處理。包含到C源程序中的頭文件可以是系統提供的,這些頭文件一般被放在/usr/include目錄下。在程序中#include它們要使用尖括弧(<>)。另外開發人員也可以定義自己的頭文件,這些文件一般與C源程序放在同一目錄下,此時在#include中要用雙引號("")。
(4)特殊符號,預編譯程序可以識別一些特殊的符號。
例如在源程序中出現的LINE標識將被解釋為當前行號(十進制數),FILE則被解釋為當前被編譯的C源程序的名稱。預編譯程序對於在源程序中出現的這些串將用合適的值進行替換。
預編譯程序所完成的基本上是對源程序的「替代」工作。經過此種替代,生成一個沒有宏定義、沒有條件編譯指令、沒有特殊符號的輸出文件。這個文件的含義同沒有經過預處理的源文件是相同的,但內容有所不同。下一步,此輸出文件將作為編譯程序的輸出而被翻譯成為機器指令。
第二個階段編譯、優化階段。經過預編譯得到的輸出文件中,只有常量;如數字、字元串、變數的定義,以及C語言的關鍵字,如main,if,else,for,while,{,}, +,-,*,\等等。
編譯程序所要作得工作就是通過詞法分析和語法分析,在確認所有的指令都符合語法規則之後,將其翻譯成等價的中間代碼表示或匯編代碼。
優化處理是編譯系統中一項比較艱深的技術。它涉及到的問題不僅同編譯技術本身有關,而且同機器的硬體環境也有很大的關系。優化一部分是對中間代碼的優化。這種優化不依賴於具體的計算機。另一種優化則主要針對目標代碼的生成而進行的。
對於前一種優化,主要的工作是刪除公共表達式、循環優化(代碼外提、強度削弱、變換循環控制條件、已知量的合並等)、復寫傳播,以及無用賦值的刪除,等等。
後一種類型的優化同機器的硬體結構密切相關,最主要的是考慮是如何充分利用機器的各個硬體寄存器存放的有關變數的值,以減少對於內存的訪問次數。另外,如何根據機器硬體執行指令的特點(如流水線、RISC、CISC、VLIW等)而對指令進行一些調整使目標代碼比較短,執行的效率比較高,也是一個重要的研究課題。
2、匯編
匯編實際上指把匯編語言代碼翻譯成目標機器指令的過程。對於被翻譯系統處理的每一個C語言源程序,都將最終經過這一處理而得到相應的目標文件。目標文件中所存放的也就是與源程序等效的目標的機器語言代碼。目標文件由段組成。通常一個目標文件中至少有兩個段:
代碼段:該段中所包含的主要是程序的指令。該段一般是可讀和可執行的,但一般卻不可寫。
數據段:主要存放程序中要用到的各種全局變數或靜態的數據。一般數據段都是可讀,可寫,可執行的。
UNIX環境下主要有三種類型的目標文件:
(1)可重定位文件
其中包含有適合於其它目標文件鏈接來創建一個可執行的或者共享的目標文件的代碼和數據。
(2)共享的目標文件
這種文件存放了適合於在兩種上下文里鏈接的代碼和數據。
第一種是鏈接程序可把它與其它可重定位文件及共享的目標文件一起處理來創建另一個 目標文件;
第二種是動態鏈接程序將它與另一個可執行文件及其它的共享目標文件結合到一起,創建一個進程映象。
(3)可執行文件
它包含了一個可以被操作系統創建一個進程來執行之的文件。匯編程序生成的實際上是第一種類型的目標文件。對於後兩種還需要其他的一些處理方能得到,這個就是鏈接程序的工作了。
二、鏈接過程
由匯編程序生成的目標文件並不能立即就被執行,其中可能還有許多沒有解決的問題。
例如,某個源文件中的函數可能引用了另一個源文件中定義的某個符號(如變數或者函數調用等);在程序中可能調用了某個庫文件中的函數,等等。所有的這些問題,都需要經鏈接程序的處理方能得以解決。
鏈接程序的主要工作就是將有關的目標文件彼此相連接,也即將在一個文件中引用的符號同該符號在另外一個文件中的定義連接起來,使得所有的這些目標文件成為一個能夠被操作系統裝入執行的統一整體。
根據開發人員指定的同庫函數的鏈接方式的不同,鏈接處理可分為兩種:
(1)靜態鏈接
在這種鏈接方式下,函數的代碼將從其所在地靜態鏈接庫中被拷貝到最終的可執行程序中。這樣該程序在被執行時這些代碼將被裝入到該進程的虛擬地址空間中。靜態鏈接庫實際上是一個目標文件的集合,其中的每個文件含有庫中的一個或者一組相關函數的代碼。
(2) 動態鏈接
在此種方式下,函數的代碼被放到稱作是動態鏈接庫或共享對象的某個目標文件中。鏈接程序此時所作的只是在最終的可執行程序中記錄下共享對象的名字以及其它少量的登記信息。在此可執行文件被執行時,動態鏈接庫的全部內容將被映射到運行時相應進程的虛地址空間。動態鏈接程序將根據可執行程序中記錄的信息找到相應的函數代碼。
對於可執行文件中的函數調用,可分別採用動態鏈接或靜態鏈接的方法。使用動態鏈接能夠使最終的可執行文件比較短小,並且當共享對象被多個進程使用時能節約一些內存,因為在內存中只需要保存一份此共享對象的代碼。但並不是使用動態鏈接就一定比使用靜態鏈接要優越。在某些情況下動態鏈接可能帶來一些性能上損害。
我們在linux使用的gcc編譯器便是把以上的幾個過程進行捆綁,使用戶只使用一次命令就把編譯工作完成,這的確方便了編譯工作,但對於初學者了解編譯過程就很不利了,下圖便是gcc代理的編譯過程:
從上圖可以看到:
預編譯
將.c 文件轉化成 .i文件
使用的gcc命令是:gcc –E
對應於預處理命令cpp
編譯
將.c/.h文件轉換成.s文件
使用的gcc命令是:gcc –S
對應於編譯命令 cc –S
匯編
將.s 文件轉化成 .o文件
使用的gcc 命令是:gcc –c
對應於匯編命令是 as
鏈接
將.o文件轉化成可執行程序
使用的gcc 命令是: gcc
對應於鏈接命令是 ld
總結起來編譯過程就上面的四個過程:預編譯、編譯、匯編、鏈接。了解這四個過程中所做的工作,對我們理解頭文件、庫等的工作過程是有幫助的,而且清楚的了解編譯鏈接過程還對我們在編程時定位錯誤,以及編程時盡量調動編譯器的檢測錯誤會有很大的幫助的。
是否可以解決您的問題?
『捌』 編譯原理簡單嗎
編譯原理主要是講了編譯器的實現。
那什麼是編譯器呢?
編譯器就是將 源程序→編譯器 →目標機器代碼的程序
本文將用一段最簡單的代碼進行說明
1 + 2 + 3
第一步. 詞法分析
當代碼從文件中被讀入到編輯器時,將會進行詞法分析
示例中的代碼最終會轉換為(下面為偽代碼)
1 ADD 2 ADD 3
第二步. 語法分析
這一步編譯器將會把詞法分析的結果轉換成AST(abstract syntax tree, 抽象語法樹)
所有的操作數將會作為子節點,所有的操作符將會作為父節點。(不知道的同學可以看一下樹的生成)
1 + 2 + 3 對應的樹
3. 生成目標代碼
對上面的樹進行後序遍歷,將會得到下面的偽代碼
((1 2 +) 3 +)
生成的匯編偽代碼為
START:
MOV VALUE, 0//初始化結果為0
ADD VALUE, 1
ADD VALUE, 2//(1 2 +)的匯編偽代碼
ADD VALUE, 3
RET VALUE
END
最終匯編代碼會被編譯成機器代碼,在計算機上執行。
下面為一般情況下的編譯流程
1. 詞法分析(生成代碼對應的token序列,使用正則表達式)
2. 語法分析(生成AST)
3. 語義分析(對代碼的語法進行檢查)
4. 代碼生成(生成可執行的代碼)
『玖』 c語言編譯tc2.0怎麼用
拷貝一些給你,一般編譯器要注意的就是include目錄和lib目錄,還有就是編譯器調用的tcc,tlink之類的要在路徑內。不過簡單的菜單還是學一下吧,學編程還是需要一些英語的。
⒈首先要確保名為turboc.CFG的文件與TCC.EXE位於同一
目錄下如同在C:\TC目錄下。turboc.CFG的全部內容是
-IC:\TC\INCLUDE
-LC:\TC\LIB
它的作用是告訴TCC.EXE,.h文件和.lib文件在哪裡。
⒉最簡單的情形——當只需要編譯連接一個C源文件
設含主函數main()的文件名為mfile.c(下同)
則用以下命令之一可使編譯、連接"一步到位":
① tcc -f87 mfile.c <回車>
② tcc mfile.c <回車>
③ tcc mfile <回車>
命令①的效果優於命令②③因為 -f87 啟動了協處理器
從而使最終得到的可執行文件mfile.exe代碼短,速度快
命令②③的效果是用軟體模擬協處理器的8087指令代碼
既長速度且慢。當然如果機器上沒有協處理器就只能采
用命令②或③了。若源文件的擴展名是c,則只須輸入文
件的主名,小數點和'c'一並省略,故命令②③效果相同
【注】如果C源文件的擴展名不是c,則又分兩種情況:
若無擴展名(例如:僅有主名mfile)則編譯連接命令應為
tcc -f87 mfile. <回車>
別忘了末尾那個小數點!
若有擴展名(例如:mfile.cpp)則編譯連接命令應為
tcc -f87 mfile.cpp <回車>
⒊較復雜的情形——數個C源文件編譯後連接為一個程序
設除了源文件mfile.c外,還有1.sub和2.fun兩個源文件
則此時"一步到位"的既編譯又連接的命令如下:
tcc [-f87] mfile[.c] 1.sub 2.fun <回車>
以上含方括弧[]的項表示可選擇項(下同)。該命令的效
果是順序編譯這些C源文件因而先後生成以下目標文件
mfile.obj,1.obj,2.obj 隨後連接並生成 mfile.exe
換言之以上命令的效果等價於下面所列命令序列的效果
tcc -c mfile[.c] <回車>
tcc -c 1.sub <回車>
tcc -c 2.fun <回車>
tcc [-f87] mfile.obj 1.obj 2.obj <回車>
應當指出:-f87 開關對於編譯不起作用,但在連接階段
將引導連接器TLINK.EXE與庫文件 C:\TC\LIB\FP87.LIB
連接而不是預設時的模擬庫文件 C:\TC\LIB\EMU.LIB。
⒋更復雜的情形——C主文件和匯編模塊連接為一個程序
設除源文件mfile.c外,還有匯編模塊sub.asm和fun.asm
此時"一步到位"的既編譯、匯編又連接的命令如下:
tcc [-f87] -B mfile[.c] sub.asm fun.asm <回車>
以上命令的效果等價於下面所列命令序列的效果:
tcc -c mfile[.c] <回車>
tasm -Mx sub[.asm] <回車>
tasm -Mx fun[.asm] <回車>
tcc [-f87] mfile.obj sub.obj fun.obj <回車>
⒌內存模式——Tiny,Small,Compact,Medium,Lagre,Huge
對應的開關是 -mt, -ms(預設), -mc, -mm, -ml, -mh
注意該開關對於編譯和對於連接都是有意義的。例如:
tcc -ml mfile[.c] <回車>
等價於這樣2條命令:
① tcc -ml -c mfile[.c] <回車>
② tcc -ml mfile.obj <回車>
命令①中的 -ml 告訴編譯器:代碼和數據都是"遠"的。
命令②中的 -ml 告訴連接器:連接MATHL.LIB和CL.LIB
⒍混合模式編程。例如:總體上是SMALL模式,但希望調用
按LARGE模式編寫的函數。這是可以做到的,關鍵在函數
聲明語句要強調代碼和數據的 far 屬性。 有這樣一個
具體例子。在MS-Fortran4.0的庫LLIBFR7.LIB中有個名
為CIXTOMX.OBJ的模塊,其中定義了4個"遠"函數。用C
語言編程調用這4個函數的關鍵是正確的函數原型聲明
extern void far fmsbintoieee(float far*,float far*);
extern void far dmsbintoieee(double far*,double far*);
extern void far fieeetomsbin(float far*,float far*);
extern void far dieeetomsbin(double far*,double far*);
這樣聲明後,即使用 -ms 進行編譯連接也能正常運行。
完整的程序見 D:\FOR4.0\ 目錄下的
MS.C,CIXTOMX.OBJ,IXTOMX.OBJ,MS.EXE,ML.C,ML.EXE。
⒎連接階段如何用TLINK.EXE完成。對於
tcc -f87 mfile.obj sub.obj fun.obj <回車>
在預設的SMALL模式下也可以用以下命令實現連接:
TLINK c0S mfile sub fun,mfile,nul,maths fp87 cs <回車>
同理對於
tcc -ml mfile.obj sub.obj fun.obj <回車>
則相當於執行下列命令:
TLINK c0L mfile sub fun,mfile,nul,maths emu cs <回車>
其中啟動代碼即C的初始化程序C0S.OBJ或C0L.OBJ可以
在C:\TC\LIB\下找到,它們的源程序在C:\TC\STARTUP\
目錄下——該目錄中的MAIN.C程序也值得關注。