歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

實現一個基於tcc/tlink的簡單的編譯鏈接工具

一、基礎研究

在這裡我們需要提供一套新的c語言開發工具cc,它支持的c程序不是從main開始運行而是從CMain開始運行。

書上已經對該工具程序進行了需求分析:(1)要在屏幕中間顯示彩色的字符串;(2)等待用戶輸入,按下任意鍵後開始運行程序員寫的程序。

也給出了由需求分析進行的功能分析:代碼文件main.obj實現打印字符串、等待輸入、調用程序的功能。編譯鏈接文件cc.exe實現調用tcc編譯文件、調用tlink連接文件的功能。

新建文件夾,在其中實現main.c如圖:

 

將main.c編譯成main.obj文件,並將tcc.exe、tlink.exe以及編譯連接所需文件都拷貝到文件夾中。

這裡我們要實現一個編譯連接程序cc.c與c.bat功能相同。

那麼首先來看看什麼是批處理文件:批處理就是對某對象進行批量的處理。批處理文件的擴展名為.bat。在“命令提示”下鍵入批處理文件的名稱,或者雙擊該批處理文件,系統就會調用Cmd.exe運行該批處理程序。我們來看看c.bat的內容:

 

這裡@表示不顯示@後面的命令,echo是一條批處理指令,echo off是關閉回顯功能,即後面的語句執行時都不會在屏幕上顯示。%1表示傳過來的參數。

但是這裡批處理使用了dos命令tcc和tlink,而dos命令是不能直接寫在c程序裡的,能否在c程序裡使用dos命令呢?我們來看system函數的功能,它可以發出一個dos命令,如我們要在c程序裡使用命令tcc main,可以用system(“tcc main”);來實現。這裡還要用到的一個函數strcat,可以把一個字符串添加到另一個字符串結尾處,覆蓋另一個字符串結尾處的'\0')並添加'\0',合成一個完整的字符串。

一個參考程序putarg.c,我們來看看它的內容:

 

這個程序的作用是將arg字符數組的值作為一個字符串輸出,而且我們要注意這裡的參數arg是一個二階指針,它的作用是什麼呢?我們來運行一下:

 

 

首先輸出了當前的絕對路徑,然後輸出了我們加在後面的參數,這說明程序是將命令行的參數名作為字符串即字符數組的形式來處理的,*arg的值為參數的字符串的首地址,而**arg的值是字符串的每一個字符。為什麼這裡n代表命令行參數的個數,arg指向參數的首地址呢?查找資料可知這是main函數不是作為普通函數來使用的,所以它的參數是有特殊用途的。main函數如果帶參有兩個參數,那麼第一個表示參數的個數;第二個參數中argv[0]為自身運行目錄路徑和程序名,argv[1]指向第一個參數、argv[2]指向第二個參數、等等。

那麼我們就可以通過使用帶參數的main函數接收我們要編譯連接的c文件名,然後將它用strcat進行處理,在用system執行進行編譯連接。

編寫程序如下:

 

這裡用數組a、b、c、d存儲批處理文件裡不用改的部分字符串。在開始進行判斷,如果n小於2,說明在命令行沒有輸入要編譯的文件名,則提示錯誤信息並返回。

之後將要編譯的文件名加在數組a的後面,形成一句完整的tcc編譯語句,將目標文件編譯成obj文件。因為arg指針指向的第一個值是本程序的絕對路徑,即arg[0]的值是本程序的絕對路徑,而arg[1]是存儲要編譯的文件的字符串的首地址。然後用system執行數組a存放的tcc編譯語句。

之後要判斷字符串是否結束,如果未結束,則判斷字符是否為’.’,因為我們如果在命令行輸入的參數是XXX.c,那麼tcc時後面加XXX.c是正確的,但是在tlink語句裡面加XXX.c是錯誤的,應該用XXX或者XXX.obj,為了簡便我們就使用名字。然後將“.”改成轉義字符“\0”表示字符串已經結束。

之後再用strcat將tlink連接語句拼接好用system執行。

執行結果如下:

 

如果不輸入文件名則提示錯誤。如果輸入則正確編譯連接。運行編譯連接生成的exe文件如圖:

 

 

先在屏幕中間顯示顯示彩色字符串,再執行CMain函數“welcome to c”,輸出字符串“hello world!”。

二、擴展研究

(1)為什麼main函數的參數是參數的個數和命令行參數字符串?

答:我認為是參數是在程序跳轉到main函數之前初始化時將命令行的參數的字符串地址傳遞到棧裡,再在main函數裡通過棧調用。

三、研究總結

今天我們自己基於tcc、tlink實現了一個編譯連接工具cc,它的功能與tcc相似,都是編譯連接程序,只是多出了顯示歡迎字符串和等待輸入再執行程序的功能。向下看,其本質還是tcc的功能,我們並沒有真正地實現一個編譯器和連接器,向上看,這個程序的實現是一個不斷集成的過程,再加上別的功能的obj文件可以組合成更大更豐富的程序。

這一章對共性和個性的分離和封裝更加清晰了:共性被封裝在編譯工具裡,個性由要編譯的程序實現,按照這個思路,我們可以開發出功能更強大的編譯連接工具。

這一張我覺得學習了一個很重要的函數system,我們可以通過程序來調用操作系統的功能,它與我們傳統的思維是不同的,我覺得操作系統打開軟件執行,軟件實現功能後返回操作系統,而這裡軟件可以調用操作系統的功能,這使我們寫的程序可以實現更強大的功能。這種操作是由頂層向底層的調用。

Copyright © Linux教程網 All Rights Reserved