TCC (TinyCC) 是一個超小型的 C 語言編譯器,其創作者是 Fabrice Bellard,你可以從 http://bellard.org/tcc/ 當中下載其最新版本。
筆者在此所使用的是 tcc-0.9.25 的版本,然後在 Windows XP 作業系統中使用 Dev C++ 所內附的 gcc 進行編譯,其過程與結果如下。
首先看看 TinyCC 專案的檔案與目錄結構。
C:\tcc>dir
磁碟區 C 中的磁碟沒有標籤。
磁碟區序號: 8C1F-9341
C:\tcc 的目錄
2011/02/18 上午 09:42 <DIR> .
2011/02/18 上午 09:42 <DIR> ..
2009/05/18 下午 10:27 325 .cvsignore
2009/05/18 下午 10:27 38,681 arm-gen.c
2009/05/18 下午 10:27 70,941 c67-gen.c
2009/05/18 下午 10:27 14,462 Changelog
2009/05/18 下午 10:27 22,419 coff.h
2011/02/18 上午 09:42 109 config.h
2009/05/18 下午 10:27 9,276 configure
2009/05/18 下午 10:27 26,428 COPYING
2009/05/18 下午 10:27 78,631 elf.h
2011/02/18 上午 09:42 <DIR> examples
2009/05/18 下午 10:27 36,588 i386-asm.c
2009/05/18 下午 10:27 21,281 i386-asm.h
2009/05/18 下午 10:27 29,033 i386-gen.c
2009/05/18 下午 10:27 16,721 il-gen.c
2009/05/18 下午 10:27 7,612 il-opcodes.h
2011/02/18 上午 09:42 <DIR> include
2011/02/18 上午 09:42 <DIR> lib
2009/05/18 下午 10:27 61,474 libtcc.c
2009/05/18 下午 10:27 3,490 libtcc.h
2009/05/18 下午 10:27 6,940 Makefile
2009/05/18 下午 10:27 2,650 README
2009/05/18 下午 10:27 9,131 stab.def
2009/05/18 下午 10:27 259 stab.h
2009/05/18 下午 10:27 107,357 tcc-doc.html
2009/05/18 下午 10:27 33,562 tcc-doc.texi
2009/05/18 下午 10:27 17,454 tcc.c
2009/05/18 下午 10:27 23,165 tcc.h
2009/05/18 下午 10:27 28,037 tccasm.c
2009/05/18 下午 10:27 22,693 tcccoff.c
2009/05/18 下午 10:27 90,590 tccelf.c
2009/05/18 下午 10:27 154,158 tccgen.c
2009/05/18 下午 10:27 47,290 tccpe.c
2009/05/18 下午 10:27 84,003 tccpp.c
2009/05/18 下午 10:27 11,439 tcctok.h
2011/02/18 上午 09:42 <DIR> tests
2009/05/18 下午 10:27 11,030 texi2pod.pl
2009/05/18 下午 10:27 3,405 TODO
2009/05/18 下午 10:27 6 VERSION
2011/02/18 上午 09:42 <DIR> win32
2009/05/18 下午 10:27 40,738 x86_64-gen.c
35 個檔案 1,131,378 位元組
7 個目錄 29,695,655,936 位元組可用
您會發現一個 win32 的資料夾,這符合我們所用的 Windows XP 平台,進入看看。
C:\tcc>cd win32
C:\tcc\win32>dir
磁碟區 C 中的磁碟沒有標籤。
磁碟區序號: 8C1F-9341
C:\tcc\win32 的目錄
2011/02/18 上午 09:42 <DIR> .
2011/02/18 上午 09:42 <DIR> ..
2009/05/18 下午 10:27 985 build-tcc.bat
2011/02/18 上午 09:42 <DIR> examples
2011/02/18 上午 09:42 <DIR> include
2011/02/18 上午 09:42 <DIR> lib
2009/05/18 下午 10:27 3,934 tcc-win32.txt
2011/02/18 上午 09:42 <DIR> tools
2 個檔案 4,919 位元組
6 個目錄 29,697,015,808 位元組可用
您會再其中發現一個 build-tcc.bat 的檔案,這是 Windows 的批次檔,讓我們執行看看。
C:\tcc\win32>build-tcc
C:\tcc\win32>gcc -Os -fno-strict-aliasing ../tcc.c -o tcc.exe -s
C:\tcc\win32>gcc -Os -fno-strict-aliasing ../libtcc.c -c -o libtcc.o
C:\tcc\win32>gcc -Os tools/tiny_impdef.c -o tiny_impdef.exe -s
C:\tcc\win32>gcc -Os tools/tiny_libmaker.c -o tiny_libmaker.exe -s
C:\tcc\win32>mkdir libtcc
C:\tcc\win32>ar rcs libtcc/libtcc.a libtcc.o
C:\tcc\win32>del libtcc.o
C:\tcc\win32>copy ..\libtcc.h libtcc
複製了 1 個檔案。
C:\tcc\win32>.\tcc -c lib/crt1.c
C:\tcc\win32>.\tcc -c lib/wincrt1.c
C:\tcc\win32>.\tcc -c lib/dllcrt1.c
C:\tcc\win32>.\tcc -c lib/dllmain.c
C:\tcc\win32>.\tcc -c lib/chkstk.S
C:\tcc\win32>.\tcc -c ../lib/libtcc1.c
C:\tcc\win32>.\tcc -c ../lib/alloca86.S
C:\tcc\win32>.\tcc -c ../lib/alloca86-bt.S
C:\tcc\win32>ar rcs lib/libtcc1.a crt1.o wincrt1.o dllcrt1.o dllmain.o chkstk.o
libtcc1.o alloca86.o alloca86-bt.o
C:\tcc\win32>del *.o
C:\tcc\win32>
賓果,我們已經建立好整個 tcc 專案了,其中 tcc 編譯器就是 tcc.exe 這個輸出檔了,讓我們用這個編譯器編譯幾個程式吧。
tcc 已經為我們準備好一些測試程式,位在 examples 這個資料夾中。但是萬事具備,只差還沒讓我們可以到處使用 tcc 這個指令。我們必須設定 PATH 路徑。請在「開始/控制台/系統/進階/環境變數」中,將 tcc.exe 所在的資料夾 (筆者的為 C:\tcc\win32) 設定到 PATH 變數當中。然後重新開啟一個命令列視窗。
example 資料夾中已經有 5 個 C 語言程式範例,讓我們一一測試看看。
範例一:ex1.c
#! /usr/local/bin/tcc -run
#include <tcclib.h>
int main()
{
printf("Hello World\n");
return 0;
}
編譯執行過程
C:\tcc\examples>tcc -I ../include ex1.c -o ex1.exe
C:\tcc\examples>ex1
Hello World
範例 2: ex2.c
#include <stdlib.h>
#include <stdio.h>
#define N 20
int nb_num;
int tab[N];
int stack_ptr;
int stack_op[N];
int stack_res[60];
int result;
int find(int n, int i1, int a, int b, int op)
{
int i, j;
int c;
if (stack_ptr >= 0) {
stack_res[3*stack_ptr] = a;
stack_op[stack_ptr] = op;
stack_res[3*stack_ptr+1] = b;
stack_res[3*stack_ptr+2] = n;
if (n == result)
return 1;
tab[i1] = n;
}
for(i=0;i<nb_num;i++) {
for(j=i+1;j<nb_num;j++) {
a = tab[i];
b = tab[j];
if (a != 0 && b != 0) {
tab[j] = 0;
stack_ptr++;
if (find(a + b, i, a, b, '+'))
return 1;
if (find(a - b, i, a, b, '-'))
return 1;
if (find(b - a, i, b, a, '-'))
return 1;
if (find(a * b, i, a, b, '*'))
return 1;
if (b != 0) {
c = a / b;
if (find(c, i, a, b, '/'))
return 1;
}
if (a != 0) {
c = b / a;
if (find(c, i, b, a, '/'))
return 1;
}
stack_ptr--;
tab[i] = a;
tab[j] = b;
}
}
}
return 0;
}
int main(int argc, char **argv)
{
int i, res, p;
if (argc < 3) {
printf("usage: %s: result numbers...\n"
"Try to find result from numbers with
the 4 basic operations.\n", argv[0]);
exit(1);
}
p = 1;
result = atoi(argv[p]);
printf("result=%d\n", result);
nb_num = 0;
for(i=p+1;i<argc;i++) {
tab[nb_num++] = atoi(argv[i]);
}
stack_ptr = -1;
res = find(0, 0, 0, 0, ' ');
if (res) {
for(i=0;i<=stack_ptr;i++) {
printf("%d %c %d = %d\n",
stack_res[3*i], stack_op[i],
stack_res[3*i+1], stack_res[3*i+2]);
}
return 0;
} else {
printf("Impossible\n");
return 1;
}
}
這個範例 ex2.c 的功能我看不太懂,但顯然參數是一堆數字,讓我們試著執行看看。
C:\tcc\examples>tcc -I ../include ex2.c -o ex2.exe
C:\tcc\examples>ex2
usage: ex2: result numbers...
Try to find result from numbers with the 4 basic operations.
C:\tcc\examples>ex2 1 2
result=1
Impossible
C:\tcc\examples>ex2 1 2 3 4
result=1
2 + 3 = 5
5 - 4 = 1
C:\tcc\examples>ex2 1 2 3 4 5 6 7
result=1
2 + 3 = 5
5 + 4 = 9
9 + 5 = 14
14 - 6 = 8
8 - 7 = 1
看來是在找加減乘除的關係。
範例 3 : ex3.c
#include <stdlib.h>
#include <stdio.h>
int fib(n)
{
if (n <= 2)
return 1;
else
return fib(n-1) + fib(n-2);
}
int main(int argc, char **argv)
{
int n;
if (argc < 2) {
printf("usage: fib n\n"
"Compute nth Fibonacci number\n");
return 1;
}
n = atoi(argv[1]);
printf("fib(%d) = %d\n", n, fib(n, 2));
return 0;
}
範例 3 顯然是用費氏序列問題在測試遞迴,其編譯執行結果如下。
C:\tcc\examples>tcc -I ../include ex3.c -o ex3.exe
C:\tcc\examples>ex3
usage: fib n
Compute nth Fibonacci number
C:\tcc\examples>ex3 5
fib(5) = 5
C:\tcc\examples>ex3 6
fib(6) = 8
C:\tcc\examples>ex3 10
fib(10) = 55
C:\tcc\examples>ex3 20
fib(20) = 6765
範例 4: ex4.c
#!./tcc -run -L/usr/X11R6/lib -lX11
#include <stdlib.h>
#include <stdio.h>
#include <X11/Xlib.h>
/* Yes, TCC can use X11 too ! */
int main(int argc, char **argv)
{
Display *display;
Screen *screen;
display = XOpenDisplay("");
if (!display) {
fprintf(stderr, "Could not open X11 display\n");
exit(1);
}
printf("X11 display opened.\n");
screen = XScreenOfDisplay(display, 0);
printf("width = %d\nheight = %d\ndepth = %d\n",
screen->width,
screen->height,
screen->root_depth);
XCloseDisplay(display);
return 0;
}
由於第 4 個範例牽涉到 XWindow,筆者電腦沒有安裝,因此無法正確編譯,其錯誤結果如下。
C:\tcc\examples>tcc -I ../include ex4.c -o ex4.exe
ex4.c:4: include file 'X11/Xlib.h' not found
範例 5: ex5.c
第 5 個範例則牽涉到標準檔頭 stdio.h 與 stdlib.h,其程式碼如下。
#include <stdlib.h>
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
由於牽涉到標準執行檔的檔頭,但是 tcc 當中又沒有內附,筆者只好利用 Dev C++ 當中所具有的檔頭,將 C:\Dev-Cpp\include 引用進來,其編譯結果如下。
C:\tcc\examples>tcc -I ../include -I C:\Dev-Cpp\include ex5.c -o ex5.exe
In file included from ex5.c:1:
C:/Dev-Cpp/include/stdlib.h:97: declaration for parameter '__argc_dll' but no su
ch parameter
這下踢到鐵板了,讓我們看看錯誤的第 97 行吧!
__MINGW_IMPORT int __argc_dll;
顯然這牽涉到 __MINGW_IMPORT 這的定義,也就是與 MINGW 有關,經過觀察之後,發現 win32 目錄下居然有 stdio.h 等檔案,於是改用下列指令進行編譯,這樣就通過編譯程序了。
C:\tcc\examples>tcc -I ../include -I ../win32/include ex5.c -o ex5.exe
C:\tcc\examples>ex5
Hello World
Facebook
陈老师你好。
我试图研究学习TCC是因为我想要得到一个很小很单纯的C解释器。
确切的说,我是一个单片机开发者,使用STM32 NRF51822这类 M0 M4量级的单片机做智能硬件。我意识到脚本功能对于单片机的应用扩展具有相当的用处(确切说是可以作为一种非常好的IAP实现方案。)
我尝试过lua,对于PC 微处理器等来说,100KBytes级别的空间算是非常小,但对于单片机而言这还是太大了。而且我发现lua的功能对于我想要的(仅仅只是一个C解释器)来说,功能还是太强大太冗余了,于是我非常希望能得到一个纯粹的解释器,只需要能解析应用文本,然后调用symbol就好了,而我希望它的体积可以控制在 Kbytes级别,甚至更小。
最后我发现了TCC这个东西,似乎是个很不错的东西,更难能可贵的是,它调用C symbol的方式似乎非常自然,不必像lua那样,重新封装它。
但是对于TCC的资料很少,而且我对于编译器没有概念,看了几天都不得要领,昨天我看tcc官网看到,已经有了对arm的port,于是我特意去搜索这个关键词,看到你写的博客,从中终于对这个东西有了更进一步的了解。
比如看你的MINGW编译例子我才明白,似乎,TCC对于脚本的引用非常特别,不同于lua,可以作为一个C模块或者库,加入项目代码里,它似乎是要把TCC编译器编译出来,然后直接像gcc编译命令那样,去编译.c文件脚本,就可以完成调用过程了。
这的确是个非常方便的C脚本解释器!
但这样的话我就有几个头疼的地方了:
1.如果是这样,但我把它作为ARM控制器(STM32等)用的时候,显然我不可能把它编译成一个单独的可执行文件,因为STM32上基本不跑WINDOWS LINUX这类系统,不会有这样的程序调用过程,那么,我是应该把它编译成一个lib文件吗?
2.作为一个编译器,不管再小,它的体积是不是也不太可能小到几Kbytes?
因为农历春节,我在家过年,手边只有一个笔记本,没有相应硬件做这个测试,还没有编译出来。
关于这些信息,不知道陈老师你是否能给我简单介绍一下?
最后非常感谢你的博客,让我对这个事情终于有了一些比较清晰的认识。
Post preview:
Close preview