TCC(TinyCC)有个特性,就是可以将C源文件像执行脚本一样直接执行,而省去了手工编译等过程。一开始我并不觉得这是一种很大的优势,因为如果编写脚本的话,Python,Perl要方便多了,尤其是处理字符串之类的。
最近我突然发现,TCC的这一特性对于做电子相关领域的人而言还真是非常有用。比如,像我们这样的万能打杂者,经常处理由FPGA,DSP什么的导出的数据(比如《从QuartusII中导出的tbl文件中提取数据并绘制波形(续)》),这些数据可能是754标准浮点型,但是通过串口一个字节一个字节传出来的,或者是由12位补码高位补零变成32位数据传出来的,或者是以十六进制形式存储的浮点数据。对于这样的任务,C语言简直就是完美的武器,但是这样的tactical mission总是在不断变化,需要特别灵活的工具才能使计划跟上变化。所以一般都是Python+Ctypes处理。虽然ctypes是很强大的操作C数据类型的工具,但是比起直接用C,还是稍微有点麻烦。
如果把gawk的管道能力和TCC直接执行C源文件的能力结合起来,则可以得到一种灵活而强大的数据处理工具。举一个例子,比如有下面一个数据文件:
1
3f800000
2
40000000
3
40400000
4
40800000
5
40a00000
6
40c00000
7
40e00000
8
41000000
其中第二列数据是以十六进制方式存储的754标准32位浮点数,现在想将第一列为奇数的所有行的第二列转换成浮点数的形式。首先用C写一段代码,将从stdin读入的十六进制数据转换成浮点形式,并从stdout输出:
接着,写gawk代码:
xialulee@xialulee-pc3 /c/lab
$ gawk '
BEGIN {
x2f = "x2f.c"
}
($1 % 2) {
print $2 |& x2f
x2f |& getline
print $0
}
END {
close(x2f)
}' data.txt
1.000000
3.000000
5.000000
7.000000
从上面的输出中,可以看出我们的目的达到了。之前为了处理这类事情,还煞有介事的写了个C程序,见《转换浮点数和字节串的控制台程序》。想一想还真是麻烦。以前还利用awk的算术运算实现了类似的功能,见《用awk将"unsigned decimal"reinterpret成"float"(续)——使用程序文件》。
再举一个例子,还是12位补码的问题,以前讨论过无数次(参见《AD补码数据和位段结构体(再续)——用weave嵌入C代码》)。AD采集的12位有符号数,高位补零之后变成了32位数据,比如0x00000ffe,对应的数值是-12。用于数据转换用C代码:
假如数据文件内容为:
ff0
ff1
ff2
ff3
ff4
ff5
ff6
ff7
执行gawk代码:
xialulee@xialulee-pc3 /c/lab
$ gawk '
> BEGIN {
>
ad12 = "ad12.c"
> }
> {
>
print $1 |& ad12
>
ad12 |& getline
>
print $0
> }
> END {
>
close(ad12)
> }' data.txt
-16
-15
-14
-13
-12
-11
-10
-9
上面的这些例子中,我们将gawk操作文本数据的能力和C操作底层数据类型的能力结合起来,把问题解决了,而且直接用tcc执行了C的源文件,而不用人工编译C程序。虽然对付上面这些简单的例子而言似乎也并没有便利多少,但是对于我们这样作为万能打杂者的特殊单位而言,灵活性常常是第一位的,尤其是对于多变的tactical mission中应用的,经常需要修改的小段C代码。当任务变得复杂而繁重时,即使一点小小的便利也能带来巨大的实惠。
附一:fflush(stdout)
如果想要C程序能够被别的程序调用,并通过stdin和stdout作为数据传输的通道,一定不要忘了在printf之后fflush(stdout)。如果不这样做,那么另一端的程序就卡住了。对于利用了C的IO库的其它语言而言,也存在同样的问题,更多细节可以参看PP3E,哪一页记不住了。
附二:Windows下用tcc
TCC同样可以在Windows下方便运行,上面的例子其实都是在Windows中做的,借助msys,和Linux下的操作没有区别。在msys的bash中用df命令查看/usr对应哪个目录,然后将tcc的可执行文件们放到/usr/local/bin中(没有就自己建立),"include"和"lib"目录放到/usr/local中,就可以了。
附三:Windows下用gawk
如果想利用gawk的管道功能将awk的文本数据处理能力和C的数据操作能力结合起来,不要用GnuWin32的gawk,因为它不支持|&运算符。msys的gawk可以。
附四:x<<20>>20?
这是什么意思?我的朋友们常常用if else这种方式判断12位补码的符号位,再根据符号位的值处理高于12位的所有位。我之前在一直用位段union这种方式来处理。其实x<<20>>20不是更简单一些吗?其实位段union处理12位补码的方法在底层也是这样实现的,我通过查看反汇编代码发现的。C中的>>运算符是逻辑右移还是算术右移呢?这和操作数的类型有关,如果被移位的变量为有符号类型,则是算术右移。先通过左移,将12位补码数据的符号位顶到32位数据单元的最高位,再用算术右移拉回来,就处理好了!
1
2
3
4
5
6
7
8
其中第二列数据是以十六进制方式存储的754标准32位浮点数,现在想将第一列为奇数的所有行的第二列转换成浮点数的形式。首先用C写一段代码,将从stdin读入的十六进制数据转换成浮点形式,并从stdout输出:
#!/usr/local/bin/tcc -run
#include <stdio.h>
// 2011.05.21 PM 09:46
// x2f.c
// xialulee
int main() {
int x;
while (scanf("%x", &x) != EOF) {
printf("%f\n", *((float*)&x));
fflush(stdout);
}
return 0;
}
接着,写gawk代码:
xialulee@xialulee-pc3 /c/lab
$ gawk '
BEGIN {
}
($1 % 2) {
}
END {
}' data.txt
1.000000
3.000000
5.000000
7.000000
从上面的输出中,可以看出我们的目的达到了。之前为了处理这类事情,还煞有介事的写了个C程序,见《转换浮点数和字节串的控制台程序》。想一想还真是麻烦。以前还利用awk的算术运算实现了类似的功能,见《用awk将"unsigned decimal"reinterpret成"float"(续)——使用程序文件》。
#!/usr/local/bin/tcc -run
#include <stdio.h>
// 2011.05.21 PM 09:54
// ad12.c
// xialulee
int main() {
int x;
while (scanf("%x", &x) != EOF) {
printf("%d\n", x<<20>>20);
fflush(stdout);
}
}
假如数据文件内容为:
ff0
ff1
ff2
ff3
ff4
ff5
ff6
ff7
执行gawk代码:
xialulee@xialulee-pc3 /c/lab
$ gawk '
> BEGIN {
>
> }
> {
>
>
>
> }
> END {
>
> }' data.txt
-16
-15
-14
-13
-12
-11
-10
-9
附一:fflush(stdout)
附二:Windows下用tcc
附三:Windows下用gawk
附四:x<<20>>20?