TinyC Compiler是个体积极小, 速度极快而十分完整的C语言编译器。此外它还提供了不输出文件,在内存中直接运行的功能,并开放了libtcc.dll模块。正因如此,它可以把c语言当成像lua一样的内嵌脚本运行。有了它,ahk也能以极快的速度在内存中直接运行c语言。
配置非常简单,只需要下载TinyCC: Index of /releases/tinycc/ (gnu.org)
选择win64版本就可以了,体积非常小,解压出来加上库也只有几m
可选:将tcc目录放到环境变量PATH中,方便以后使用。或者也可以在每次使用前加上这两句:
libtccPath := "D:\Program Files\tcc" ; 填写你的tcc目录路径
EnvSet("PATH", EnvGet("PATH") ";" libtccPath)
环境配置好后,使用下面这个函数就能运行C语言代码了:
TCScript(script, funcName, params*) {
if !libtcc := DllCall("LoadLibraryW", "str", "libtcc", "ptr")
throw OSError()
try {
if !tcc := DllCall("libtcc\tcc_new", "ptr")
throw Error("TCC: failed to create TCC compilation context")
DllCall("libtcc\tcc_set_output_type", "ptr", tcc, "int", 1)
if DllCall("libtcc\tcc_compile_string", "ptr", tcc, "astr", script) == -1
throw Error("TCC: compile error")
if DllCall("libtcc\tcc_relocate", "ptr", tcc, "ptr", 1) < 0
throw Error("TCC: relocate error")
if !f := DllCall("libtcc\tcc_get_symbol", "ptr", tcc, "astr", funcName)
throw Error("TCC: undefined symbol")
res := DllCall(f, params*)
}
catch as e
throw e
finally {
if tcc
DllCall("libtcc\tcc_delete", "ptr", tcc)
DllCall("FreeLibrary", "ptr", libtcc)
}
return res
}
使用非常简单,script参数为c语言脚本字符串,funcName是脚本中你想运行的函数名称,params是需要传递给函数的参数类型和参数值,以及可选的返回类型。
srcipt := "
(
int foo(int num){
return num * 2;
}
)"
MsgBox TCScript(srcipt, "foo", "int", 100) ; 输出200
srcipt := "
(
#include <math.h>
float GetPI(){
float s = 1, pi = 0, i = 1, n = 1;
while(fabs(i) >= 1e-6){
pi+=i;
n = n + 2;
s = -s;
i = s / n;
}
return 4 * pi;
}
)"
MsgBox TCScript(srcipt, "GetPI", "float") ; 输出3.1415939331054688
script := "
(
#include <windows.h>
int APIENTRY WinMain() {} // 使用Windows API时需要添加WinMain入口函数
void MsgBox(wchar_t *msg, wchar_t *title){
MessageBoxW(0, msg, title, MB_OK);
}
)"
TCScript(script, "MsgBox", "str", "Hello TCC!", "str", A_ScriptName)
怎样?和使用DllCall非常相似,只是多了一个script参数。
TCC脚本相对AHK代码的优势:
在内存中直接编译运行:你可以用InputBox输入一串数学表达式,立刻就能出结果。
input := InputBox() ; 输入(1 + 2) * 3
MsgBox TCScript("int calc(){return " input.Value ";}", "calc") ; 输出9
速度非常快:循环一千万次简单运算:
foo(a, b) {
c := 0
loop 10000000
c := (a + b) * (a - b)
return c
}
c := "
(
int foo(int a, int b) {
int c;
for (int i = 0; i < 10000000; i++) {
c = (a + b) * (a - b);
}
return c;
}
)"
t1 := A_TickCount
res1 := foo(10, 5)
t2 := A_TickCount
res2 := TCScript(c, "foo", "int", 10, "int", 5)
t3 := A_TickCount
MsgBox "ahk:`t" res1 "`t耗时:`t" t2 - t1 "ms" . "`ntcc:`t" res2 "`t耗时:`t" t3 - t2 "ms"
; ---------------------------
; example.ahk
; ---------------------------
; ahk: 75 耗时: 2015ms
; tcc: 75 耗时: 31ms
; ---------------------------
; 确定
; ---------------------------
斐波那契数列递归计算:
fib(n) {
if n <= 2
return 1
return fib(n - 1) + fib(n - 2)
}
c := "
(
int fib(n) {
if (n <= 2)
return 1;
return fib(n - 1) + fib(n - 2);
}
)"
t1 := A_TickCount
res1 := fib(30)
t2 := A_TickCount
res2 := TCScript(c, "fib", "int", 30)
t3 := A_TickCount
MsgBox "ahk:`t" res1 "`t耗时:`t" t2 - t1 "ms" . "`ntcc:`t" res2 "`t耗时:`t" t3 - t2 "ms"
; ---------------------------
; example.ah2
; ---------------------------
; ahk: 832040 耗时: 984ms
; tcc: 832040 耗时: 16ms
; ---------------------------
; 确定
; ---------------------------
以上,TCC是个非常棒的编译器,如果你只是想轻度体验,上面一个函数就足够了,如果你还想要接触更多的功能,可以参考官方文档:Tiny C Compiler Reference Documentation (bellard.org)
我还为libtcc封装了TinyCC for ahk类库,适当使用可以获得完整的tcc功能,更多编译选项,以及更高的运行效率,欢迎探索:
CONST := CONST ?? {},
CONST.TCC_OUTPUT_MEMORY := 1, ; output will be run in memory (default)
CONST.TCC_OUTPUT_EXE := 2, ; executable file
CONST.TCC_OUTPUT_DLL := 3, ; dynamic library
CONST.TCC_OUTPUT_OBJ := 4, ; object file
CONST.TCC_OUTPUT_PREPROCESS := 5, ; only preprocess (used internally) ; only preprocess (used internally)
CONST.TCC_RELOCATE_AUTO := 1 ; Allocate and manage memory internally
class TinyCC {
static LibTccPath := "libtcc.dll"
/* create a new TCC compilation context */
__New() {
if !this.LibTcc := DllCall("LoadLibraryW", "str", TinyCC.LibTccPath, "ptr")
throw OSError()
if !this.Ptr := DllCall("libtcc\tcc_new", "ptr")
throw Error("Failed to create TCC compilation context")
}
/* free a TCC compilation context */
__Delete() {
if this.HasOwnProp("Ptr") && this.Ptr
DllCall("libtcc\tcc_delete", "ptr", this.Ptr)
if this.HasOwnProp("LibTcc") && this.LibTcc
DllCall("FreeLibrary", "ptr", this.LibTcc)
}
/* set CONFIG_TCCDIR at runtime */
SetLibPath(path) => DllCall("libtcc\tcc_set_lib_path", "ptr", this, "astr", path)
/* set error/warning display callback */
SetErrorFunc(errorOpaque, errorFunc) => DllCall("libtcc\tcc_set_error_func", "ptr", this, "ptr", errorOpaque, "ptr", errorFunc)
/* set options as from command line (multiple supported) */
SetOptions(str) => DllCall("libtcc\tcc_set_options", "ptr", this, "astr", str)
/* add include path */
AddIncludePath(pathname) => DllCall("libtcc\tcc_add_include_path", "ptr", this, "astr", pathname)
/* add in system include path */
AddSysIncludePath(pathname) => DllCall("libtcc\tcc_add_sysinclude_path", "ptr", this, "astr", pathname)
/* define preprocessor symbol 'sym'. Can put optional value */
DefineSymbol(sym, value) => DllCall("libtcc\tcc_define_symbol", "ptr", this, "astr", sym, "astr", value)
/* undefine preprocess symbol 'sym' */
UndefineSymbol(sym) => DllCall("libtcc\tcc_undefine_symbol", "ptr", this, "astr", sym)
/* add a file (C file, dll, object, library, ld script). Return -1 if error. */
AddFile(filename) => DllCall("libtcc\tcc_add_file", "ptr", this, "astr", filename)
/* compile a string containing a C source. Return -1 if error. */
CompileString(buf) => DllCall("libtcc\tcc_compile_string", "ptr", this, "astr", buf)
/* set output type. MUST BE CALLED before any compilation */
SetOutputType(outputType) => DllCall("libtcc\tcc_set_output_type", "ptr", this, "int", outputType)
/* equivalent to -Lpath option */
AddLibraryPath(pathname) => DllCall("libtcc\tcc_add_library_path", "ptr", this, "astr", pathname)
/* the library name is the same as the argument of the '-l' option */
AddLibrary(libraryname) => DllCall("libtcc\tcc_add_library", "ptr", this, "astr", libraryname)
/* add a symbol to the compiled program */
AddSymbol(name, val) => DllCall("libtcc\tcc_add_symbol", "ptr", this, "astr", name, "ptr", val)
/* output an executable, library or object file. DO NOT call tcc_relocate() before. */
OutputFile(filename) => DllCall("libtcc\tcc_output_file", "ptr", this, "astr", filename)
/* link and run main() function and return its value. DO NOT call tcc_relocate() before. */
Run(args*) {
if args.Length {
bytes := 0
for v in args
bytes += StrPut(v, "cp0")
offset := args.Length * A_PtrSize
argv := Buffer(offset + bytes)
for v in args {
p := argv.Ptr + offset
NumPut("ptr", p, argv, (A_Index - 1) * A_PtrSize)
offset += StrPut(v, p, "cp0")
}
}
else
argv := 0
return DllCall("libtcc\tcc_run", "ptr", this, "int", args.Length, "ptr", argv, "int")
}
/* do all relocations (needed before using tcc_get_symbol()) */
/* possible values for 'ptr':
- TCC_RELOCATE_AUTO : Allocate and manage memory internally
- NULL : return required memory size for the step below
- memory address : copy code to memory passed by the caller
returns -1 if error. */
Relocate(ptr) => DllCall("libtcc\tcc_relocate", "ptr", this, "ptr", ptr)
/* return symbol value or NULL if not found */
GetSymbol(name) => DllCall("libtcc\tcc_get_symbol", "ptr", this, "astr", name, "ptr")
}
TinyCC for ahk类库使用示例,传递命令行参数,从main入口函数开始运行:
script := "
(
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv){
printf("Hello TinyCC!\n");
int res = 0;
for (int i = 0; i < argc; i++) {
res += atoi(argv[i]);
}
return res;
}
)"
tcc := TinyCC() ; 创建编译器环境
tcc.SetOutputType(CONST.TCC_OUTPUT_MEMORY) ; 编译到内存中, 不输出到硬盘
tcc.SetErrorFunc(0, CallbackCreate(ReportError)) ; 设置Error回调, 当出现编译错误时弹窗提示
ReportError(opaque, msg){
MsgBox StrGet(msg, "cp0")
}
if -1 != tcc.CompileString(script) { ; 开始编译
exitCode := tcc.Run("10", "11", "12", "100") ; 传递命令行参数,运行, 获取退出返回值
MsgBox exitCode
}
看起来非常强大,可惜我用的ahk是V1版,如果大佬能出一个ahkV1版的就好了