TCC研究(2) 把C語言當作腳本,解釋執(zhí)行,并嵌入各類程序
來源:程序員人生 發(fā)布時間:2015-04-28 08:46:52 閱讀次數(shù):4027次
Tiny C Compiler(簡稱TCC, 或Tiny CC)是世界上最小的C語言編譯器。
TCC有1個突出的特點:就是可以把C語言當作腳本使用。試用記錄以下:
首先,安裝好TCC.
在Windows下, 下載履行程序: tcc-0.9.26-win32-bin.zip。
解壓到c: cc, 將c: cc添加到PATH目錄中。
測試安裝是不是成功,在命令行窗口中打入命令 tcc -v , 看到TCC版本號即是成功
方式1: 以TCC解釋履行C語言文本
解釋履行,就是不編譯,直接運行。
寫1段C程序,存盤為 hello.c
#include <stdio.h>
int main(int argc, char *argv[]) {
int i;
printf("Hello, world
");
for(i=1; i<argc; i++)
printf("argv[%d]=%s
", i, argv[i]);
}
在命令行窗口,打入命令 tcc -run hello.c
-run 的意思是立即履行
運行結果:
Hello, world
還可以通過命令行,向程序傳入?yún)?shù)
在命令行窗口,打入命令 tcc -run hello.c param1 param2
運行結果(顯示有兩個傳入?yún)?shù)):
Hello, world
argv[1]=param1
argv[2]=param2
方式2: 在Linux下,把C語言程序當作腳本運行,像SH腳本1樣
首先,在Linux下安裝TCC,我用的版本是Ubuntu 14.04
下載TCC源碼,解壓
cd ~
wget http://download.savannah.gnu.org/releases/tinycc/tcc-0.9.26.tar.bz2
tar xvf tcc-0.9.26.tar.bz2
則生成1個子目錄 tcc-0.9.26,進入目錄,編譯它
cd tcc-0.9.26
./configure
make
make install
完成后,輸入命令 tcc -v . 如顯示tcc版本號,表示成功。
用find命令查找1下 tcc 在哪? find / -name tcc
發(fā)現(xiàn)tcc可履行文件安裝在 /usr/local/bin
寫1段C程序,存盤為 hello.c
#!/usr/local/bin/tcc -run
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Hello, world
");
}
第1行的 #!/usr/local/bin/tcc -run 是告知操作系統(tǒng),這是1個腳本,解釋器是 /usr/local/bin/tcc
修改權限,將hello.c 轉為可履行文件
chmod +x hello.c
直接運行hello.c
./hello.c
運行結果:
hello,world
感覺不錯, .c文件直接當腳本
對照1下, 編譯后再履行的情況 ,把hello.c編譯1下: tcc hello.c , 將生成 a.out
運行a.out: ./a.out
運行結果與腳本履行1樣
由于有第1行的 #!/usr/local/bin/tcc -run, 用gcc編譯hello.c會出錯,用tcc編譯就沒問題
方式3:在程序中嵌入腳本功能,動態(tài)調(diào)用C語言腳本(這是TCC的精華的地方)
嵌入腳本,就是讓你的程序具有腳本功能,而且這個腳本還是C語言的。
tcc目錄下的 exampleslibtcc_test.c是1個示范程序。
我覺得示范程序不通用,因而自己編了1個通用的模塊,兩個文件: cscript.c, cscript.h
先看 cscript.c, 定義了1個函數(shù) run_script()
說明1下: libtcc.h 是 tcc提供的1個頭文件,在tcc目錄下復制過來的
#include <stdlib.h>
#include "libtcc.h" //TCC提供的頭文件
/* 運行1個腳本, 啟動腳本中指定的函數(shù),返回該函數(shù)的運行結果值
* 腳本中的啟動函數(shù)原型必須為: int func(int param)
* program是腳本全部內(nèi)容, function_name是啟動函數(shù)名稱,param是傳遞給函數(shù)的參數(shù)
* tcc_path用于指定tcc所在目錄,tcc_path設為時NULL表示不指定tcc目錄
*/
int run_script(char *program, char *function_name, int param, char *tcc_path)
{
TCCState *s; //TCC編譯引擎
int (*func)(int); //1個函數(shù)指針, 函數(shù)原型為: int func(int param)
int result; //運行結果
s = tcc_new(); //初始化TCC編譯引擎
if (!s) return ⑴; //初始化失敗
//tcc_path指定tcc所在的目錄
if (tcc_path!=NULL) tcc_set_lib_path(s, tcc_path);
//指明編譯結果寫入內(nèi)存,而不是存為文件
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
//如果編譯失敗,則退出
if (tcc_compile_string(s, program) == ⑴) {
tcc_delete(s); return ⑵;
}
//如果程序重定位失敗,退出
if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0) {
tcc_delete(s); return ⑶;
}
//尋覓名為function_name的入口函數(shù)
func = tcc_get_symbol(s, function_name);
if (!func) { //找不到入口函數(shù),則退出
tcc_delete(s); return ⑷;
}
//運行入口函數(shù),取得運行結果
result = func(param);
//結束TCC編譯引擎
tcc_delete(s);
return result;
}
再看 cscript.h ,不過聲明了1下run_script()這個函數(shù)。
#ifndef __CSCRIPT_H__
#define __CSCRIPT_H__
#ifdef __cplusplus
extern "C" {
#endif
/** 運行1個腳本, 啟動腳本中指定的函數(shù),返回該函數(shù)的運行結果值
* 腳本中的啟動函數(shù)原型必須為: int func(int param)
* programe是腳本程序, function_name是啟動函數(shù),param是傳遞給函數(shù)的參數(shù)
* tcc_path用于指定tcc所在目錄,tcc_path設為NULL時表示不指定tcc目錄
*/
extern int run_script(char *program, char *function_name, int param, char *tcc_path);
#ifdef __cplusplus
}
#endif
#endif
好了, 編1個主程序,使用通用模塊中的run_script()函數(shù),每次通過命令行指定腳本文件,讀出并運行。
假定主程序名為 cscript.exe
運行命令為: cscript <script_file>
主程序以下,存盤為 main.c
#include <stdio.h>
#include <stdlib.h>
#include "cscript.h" //通用模塊頭文件
int main(int argc, char *argv[])
{
char *program = NULL; //腳本內(nèi)容
char *function_name = "script_main"; //啟動函數(shù)名為script_main
int param = 888; //傳入?yún)?shù)
char *tcc_path = NULL;
int file_size;
int result; //返回結果
FILE *fp;
if (argc<=1) {
printf("Usage: cscript <script_file>
");
return ⑴;
}
//第1個命令行參數(shù)是腳本文件名,打開它
if ((fp=fopen(argv[1],"rb"))==NULL) {
printf("Error open file %s
", argv[1]);
return ⑴;
}
//測出文件長度
fseek(fp, 0L, SEEK_END);
file_size = ftell(fp);
//申請1個內(nèi)存用于寄存文件內(nèi)容
program = (char *)malloc(file_size+1);
if (program==NULL) return ⑴;
//將文件內(nèi)容全部讀入到 program中
fseek(fp, 0L, SEEK_SET);
if (fread(program, file_size, 1, fp)>0) {
program[file_size]=0;
result = run_script(program, function_name, param, tcc_path);//運行
printf("result = %d
", result);
}
free(program);
fclose(fp);
}
這個程序比較簡單,不過是從命令行第1個參數(shù)指定的文件中,讀出內(nèi)容,當作腳本履行。
腳本中需要指定1個啟動函數(shù),這個函數(shù)不是main(), 而是 int script_main(int param)
編譯主程序(main.c 和 cscript.c), 假定tcc安裝在c: cc目錄下
tcc -llibtcc -Lc: cc main.c cscript.c -o cscript.exe
-llibtcc 表示鏈接 libtcc庫 (大小寫不要寫錯)
-Lc: cc 指定tcc目錄(大小寫不要寫錯)
-o cscript.exe 指定生成exe文件為 cscript.exe
編譯成功,沒有任何提示。目錄下生成 cscript.exe
好了,寫1個腳本(內(nèi)容以下),存盤為 test1.txt
#include <stdio.h>
int script_main(int param)
{
printf("it's in script main, param =%d
", param);
return param;
}
用剛才生成的 cscript.exe 直接運行 test1.txt
打入命令 cscript test1.txt
運行結果:
it's in script main, param =888
result = 888
成功!!
這1次,我們不是使用tcc去運行腳本,而是采取剛才自己寫的 cscript.exe程序去運行腳本。讓自己的程序具有了腳本功能。
從TCC提供的example上看,TCC可以支持標準C, windows API調(diào)用,Linux動態(tài)庫調(diào)用等,就是說,用腳本寫系統(tǒng)程序、GUI,甚么都行。
相當于遠程分發(fā)程序。
如何利用,看想像力了。比如說: 程序從網(wǎng)站下載1個文本,運行,生成1個GUI,產(chǎn)生1個小人在桌面上跑來跑去……
再比如:大數(shù)據(jù)運算,100臺計算機聯(lián)網(wǎng),某臺機分發(fā)1個腳本,各機本地運算,返回結果給主機匯總,就是大數(shù)據(jù)的map-reduce算法嘛。
心有多遠,就有多遠
TCC就是強!
生活不易,碼農(nóng)辛苦
如果您覺得本網(wǎng)站對您的學習有所幫助,可以手機掃描二維碼進行捐贈