Zen C 是一種現代系統編程語言,可編譯為人類可讀的 GNU C/C11。它提供了一套豐富的特性,包括類型推斷、模式匹配、泛型、Trait、async/await 以及具有 RAII 能力的手動內存管理,同時保持 100% 的 C ABI 兼容性。
加入官方 Zen C Discord 服務器,參與討論、展示 Demo、提問或報告 Bug!
Zen C 項目包含多個倉庫。下面是主要的倉庫列表:
| 倉庫 | 描述 | 狀態 |
|---|---|---|
| zenc | Zen C 核心編譯器 (zc)、CLI 和標準庫。 |
活躍開發 |
| docs | 官方技術文檔與語言規範。 | 活躍 |
| rfcs | 徵求意見稿 (RFC) 倉庫。塑造語言的未來。 | 活躍 |
| vscode-zenc | 官方 VS Code 擴充功能(語法高亮、程式碼片段)。 | Alpha |
| www | zenc-lang.org 的源代碼。 |
活躍 |
| awesome-zenc | 精選的 Zen C 範例列表。 | 不斷增加 |
查看這些使用 Zen C 構建的項目:
- ZC-pong-3ds: Nintendo 3DS 上的 Pong 克隆版。
- zen-c-parin: 使用 Parin 的 Zen C 基礎範例。
- almond: 用 Zen C 編寫的極簡網頁瀏覽器。
| 通用 | 語言參考 |
|---|---|
git clone https://github.com/zenc-lang/zenc.git
cd Zen-C
make clean # 移除舊的構建文件
make
sudo make installZen C 對 Windows (x86_64) 提供完備的原生支援。你可以使用提供的批处理脚本配合 GCC (MinGW) 進行構建:
build.bat這將構建編譯器 (zc.exe)。網路、檔案系統和程序操作通過平台抽象層 (PAL) 得到完全支援。
或者,如果你有類 Unix 環境(MSYS2、Cygwin、git-bash),也可以使用 make。
Zen C 可以通過 Cosmopolitan Libc 編譯為 Actually Portable Executable (APE)。這將生成一個單個的可執行文件 (.com),能夠原生運行在 Linux, macOS, Windows, FreeBSD, OpenBSD, 和 NetBSD 上的 x86_64 和 aarch64 架構上。
前提條件:
cosmocc工具鏈(必須在 PATH 中)
構建與安裝:
make ape
sudo env "PATH=$PATH" make install-ape產物:
out/bin/zc.com: 便攜式 Zen-C 編譯器。已將標準庫嵌入到可執行文件中。out/bin/zc-boot.com: 一個自包含的引導安裝程序,用於設置新的 Zen-C 項目。
用法:
# 在任何支持的操作系統上運行
./out/bin/zc.com build hello.zc -o hello# 編譯並運行
zc run hello.zc
# 構建可執行文件
zc build hello.zc -o hello
# 交互式 Shell
zc repl
# 顯示 Zen Facts
zc build hello.zc --zen你可以設置 ZC_ROOT 來指定標準庫的位置(標準導入如 import "std/vector.zc")。這允許你從任何目錄運行 zc。
export ZC_ROOT=/path/to/Zen-CZen C 區分編譯時常量和運行時變量。
僅在編譯時存在的值(折疊到代碼中)。用於數組大小、固定配置和魔術數字。
def MAX_SIZE = 1024;
let buffer: char[MAX_SIZE]; // 有效的數組大小
內存中的存儲位置。可以是可變的或只讀的 (const)。
let x = 10; // 可變
x = 20; // 允許
let y: const int = 10; // 只讀 (類型修飾)
// y = 20; // 錯誤:無法賦值給 const 變量
Tip
型別推導:Zen C 自動推導初始化變數的型別。在支援的編譯器上編譯為 C23 的 auto,否則使用 GCC 的 __auto_type 擴充功能。
| 類型 | C 等效類型 | 描述 |
|---|---|---|
int, uint |
int32_t, uint32_t |
32位元有號/無號整數 |
c_char, c_uchar |
char, unsigned char |
C char (互操作) |
c_short, c_ushort |
short, unsigned short |
C short (互操作) |
c_int, c_uint |
int, unsigned int |
C int (互操作) |
c_long, c_ulong |
long, unsigned long |
C long (互操作) |
c_long_long, c_ulong_long |
long long, unsigned long long |
C long long / unsigned long long (互操作) |
I8 .. I128 或 i8 .. i128 |
int8_t .. __int128_t |
有符號固定寬度整數 |
U8 .. U128 或 u8 .. u128 |
uint8_t .. __uint128_t |
無符號固定寬度整數 |
isize, usize |
ptrdiff_t, size_t |
指針大小的整數 |
byte |
uint8_t |
U8 的別名 |
F32, F64 或 f32, f64 |
float, double |
浮點數 |
bool |
bool |
true 或 false |
char |
char |
單個字符 |
string |
char* |
C-string (以 null 結尾) |
U0, u0, void |
void |
空類型 |
iN (例 i256) |
_BitInt(N) |
任意位元寬度有號整數 (C23) |
uN (例 u42) |
unsigned _BitInt(N) |
任意位寬無號整數 (C23) |
- 整數: 十進制 (
123), 十六進制 (0xFF), 八進制 (0o755), 二進制 (0b1011).- 注意: 帶有前導零的數字被視為十進制(
0123即123),這與 C 語言不同。 - 注意: 數字可以包含底線以提高可讀性 (
1_000_000,0b_1111_0000).
- 注意: 帶有前導零的數字被視為十進制(
- 浮點數: 標準 (
3.14), 科學記數法 (1e-5,1.2E3). 浮點數也支援底線 (3_14.15_92).
Important
可移植代碼最佳實踐
- 對於所有純 Zen C 邏輯,請使用 可移植類型 (
int、uint、i64、u8等)。int保證在所有架構上都是 32 位元有號整數。 - 僅在與 C 庫 (FFI) 交互時使用 C 互操作類型 (
c_int、c_char、c_long,c_ulong,c_long_long,c_ulong_long)。它們的大小因平台和 C 編譯器而異。 - 使用
isize和usize進行數組索引和內存指針運算。
具有值語義的固定大小數組。
def SIZE = 5;
let ints: int[SIZE] = [1, 2, 3, 4, 5];
let zeros: [int; SIZE]; // 零初始化的
將多個值組合在一起,通過索引訪問元素。
let pair = (1, "Hello");
let x = pair.0; // 1
let s = pair.1; // "Hello"
多個返回值
函數可以返回元組以提供多個結果:
fn add_and_subtract(a: int, b: int) -> (int, int) {
return (a + b, a - b);
}
let result = add_and_subtract(3, 2);
let sum = result.0; // 5
let diff = result.1; // 1
解構
元組可以直接解構為多個變量:
let (sum, diff) = add_and_subtract(3, 2);
// sum = 5, diff = 1
帶類型的解構允許顯式類型注解:
let (a: string, b: u8) = ("hello", 42);
let (x, y: i32) = (1, 2); // 混合:x 推斷,y 顯式
帶有可選位域的數據結構。
struct Point {
x: int;
y: int;
}
// 結構體初始化
let p = Point { x: 10, y: 20 };
// 位域
struct Flags {
valid: U8 : 1;
mode: U8 : 3;
}
Note
結構体默認使用 移動語義。即使是指針,也可以通過 . 訪問字段(自動解引用)。
你可以將結構體定義為 opaque,以將對其字段的訪問限制在定義該結構體的模塊內部,同時仍允許在棧上分配該結構體(大小已知)。
// 在 user.zc 中
opaque struct User {
id: int;
name: string;
}
fn new_user(name: string) -> User {
return User{id: 1, name: name}; // 允許:在模塊內部
}
// 在 main.zc 中
import "user.zc";
fn main() {
let u = new_user("Alice");
// let id = u.id; // 錯誤:無法訪問私有字段 'id'
}
能夠持有數據的標籤聯合 (Sum types)。
enum Shape {
Circle(float), // 持有半徑
Rect(float, float), // 持有寬、高
Point // 不帶數據
}
標準 C 聯合體(不安全訪問)。
union Data {
i: int;
f: float;
}
使用 GCC/Clang 向量擴展的原生 SIMD 向量類型。使用 @vector(N) 註解一個結構體來定義包含 N 個元素的向量。
import "std/simd.zc";
fn main() {
let a = f32x4{v: 1.0}; // 廣播: {1.0, 1.0, 1.0, 1.0}
let b = f32x4{1.0, 2.0, 3.0, 4.0}; // 逐元素初始化
let c = a + b; // 逐元素加法
let x = c[0]; // 元素存取 (float)
}
算術運算符(+, -, *, /)和位元運算符(&, |, ^)逐元素運算。預定義類型請參閱 std/simd.zc。
為現有類型創建新名稱。
alias ID = int;
alias PointMap = Map<string, Point>
alias OpFunc = fn(int, int) -> int
注意: 型別別名結尾的分號是可選項的。
你可以將類型別名定義為 opaque,從而在定義模塊之外創建一個與基礎類型不同的新類型。這提供了強大的封裝和類型安全性,而沒有包裝結構體的運行時開銷。
// 在 library.zc 中
opaque alias Handle = int;
fn make_handle(v: int) -> Handle {
return v; // 允許在模塊內部進行隱式轉換
}
// 在 main.zc 中
import "library.zc";
fn main() {
let h: Handle = make_handle(42);
// let i: int = h; // 錯誤:類型驗證失敗
// let h2: Handle = 10; // 錯誤:類型驗證失敗
}
fn add(a: int, b: int) -> int {
return a + b;
}
// 調用時支持命名參數
add(a: 10, b: 20);
Note
命名參數必須嚴格遵循定義的參數順序。add(b: 20, a: 10) 是無效的。
函數參數可以標記為 const 以強制執行只讀語義。這是一個類型修飾符,而不是清單常量。
fn print_val(v: const int) {
// v = 10; // 錯誤:無法賦值給 const 變量
println "{v}";
}
函數可以為尾部參數定義默認值。這些值可以是字面量、表達式或有效的 Zen C 代碼(如結構體構造函數)。
// 簡單默認值
fn increment(val: int, amount: int = 1) -> int {
return val + amount;
}
// 表達式默認值(在調用處計算)
fn offset(val: int, pad: int = 10 * 2) -> int {
return val + pad;
}
// 結構體默認值
struct Config { debug: bool; }
fn init(cfg: Config = Config { debug: true }) {
if cfg.debug { println "調試模式"; }
}
fn main() {
increment(10); // 11
offset(5); // 25
init(); // 打印 "調試模式"
}
可以捕獲環境的匿名函數。
let factor = 2;
let doubler = x -> x * factor; // 箭頭語法
let full = fn(x: int) -> int { return x * factor; }; // 區塊語法
// 引用捕獲(區塊語法)
let val = 10;
let modify = fn[&]() { val += 1; };
modify(); // val 現在是 11
// 引用捕獲(箭頭語法)
let modify_arrow = [&] x -> val += x;
modify_arrow(5); // val 現在是 16
// 引用捕獲(多參數箭頭語法)
let sum_into = [&] (a, b) -> val += (a + b);
sum_into(2, 2); // val 現在是 20
// 值捕獲(默認)
let original = 100;
let implicit = x -> original + x; // 隱式值捕獲(無括號)
let explicit = [=] x -> original + x; // 顯式值捕獲
// let fail = x -> original += x; // 錯誤:無法賦值給捕獲的值
Zen C 使用 fn* 語法支持原始 C 函數指針。這允許與期望函數指針且沒有閉包開銷的 C 庫進行無縫互操作。
// 接受原始函數指針的函數
fn set_callback(cb: fn*(int)) {
cb(42);
}
// 返回原始函數指針的函數
fn get_callback() -> fn*(int) {
return my_handler;
}
// 支持指向函數指針的指針 (fn**)
let pptr: fn**(int) = &ptr;
函數可以使用 ... 和 va_list 類型接受可變數量的參數。
fn log(lvl: int, fmt: char*, ...) {
let ap: va_list;
va_start(ap, fmt);
vprintf(fmt, ap); // 使用 C stdio
va_end(ap);
}
if x > 10 {
print("Large");
} else if x > 5 {
print("Medium");
} else {
print("Small");
}
// 三元運算符
let y = x > 10 ? 1 : 0;
// If-表達式 (用於複雜條件)
let 類別 = if (x > 100) { "巨大" } else if (x > 10) { "大" } else { "小" };
switch 的強大替代方案。
match val {
1 => { print "One" },
2 || 3 => { print "Two or Three" }, // 使用 || 進行 或 操作
4 or 5 => { print "Four or Five" }, // 使用 'or' 進行 或 操作
6, 7, 8 => { print "Six to Eight" }, // 使用逗號進行 或 操作
10 .. 15 => { print "10 to 14" }, // 左閉右開區間 (舊語法)
10 ..< 15 => { print "10 to 14" }, // 左閉右開區間 (顯式)
20 ..= 25 => { print "20 to 25" }, // 全閉區間
_ => { print "Other" },
}
// 解構枚舉
match shape {
Shape::Circle(r) => { println "半徑: {r}" },
Shape::Rect(w, h) => { println "面積: {w*h}" },
Shape::Point => { println "點" },
}
為了在不獲取所有權(移動)的情況下檢查一個值,在模式中使用 ref 關鍵字。這對於實現了移動語義的類型(如 Option, Result, 非 Copy 結構體)至關重要。
let opt = Some(NonCopyVal{...});
match opt {
Some(ref x) => {
// 'x' 是指向 'opt' 內部值的指針
// 'opt' 在此處不會被移動/消耗
println "{x.field}";
},
None => {}
}
// 區間迭代
for i in 0..10 { ... } // 左閉右開 (0 到 9)
for i in 0..<10 { ... } // 左閉右開 (顯式)
for i in 0..=10 { ... } // 全閉 (0 到 10)
for i in 0..10 step 2 { ... }
for i in 10..0 step -1 { ... } // Descending loop
// 迭代器 (Vec 或自定義 Iterable)
for item in vec { ... }
// 枚舉:獲取索引和值
for i, val in arr { ... } // i = 0, 1, 2, ...
for i, val in 0..10 step 2 { ... } // i = 0, 1, 2, ...; val = 0, 2, 4, ...
// 直接迭代固定大小數組
let arr: int[5] = [1, 2, 3, 4, 5];
for val in arr {
// val 是 int
println "{val}";
}
// While 循環
while x < 10 { ... }
// 帶標籤的無限循環
outer: loop {
if done { break outer; }
}
// 重複 N 次
for _ in 0..5 { ... }
// Guard: 如果條件為假,則執行 else 塊並返回
guard ptr != NULL else { return; }
// Unless: 除非為真(即如果為假)
unless is_valid { return; }
Zen C 通過實現特定的方法名來支持用戶定義結構體的運算符重載。
| 類別 | 運算符 | 方法名 |
|---|---|---|
| 算術 | +, -, *, /, %, ** |
add, sub, mul, div, rem, pow |
| 比較 | ==, != |
eq, neq |
<, >, <=, >= |
lt, gt, le, ge |
|
| 位運算 | &, ` |
, ^` |
<<, >> |
shl, shr |
|
| 一元 | - |
neg |
! |
not |
|
~ |
bitnot |
|
| 索引 | a[i] |
get(a, i) |
a[i, j] |
get(a, i, j) |
|
a[i] = v |
set(a, i, v) |
Note
關於字符串相等性的說明:
string == string進行 值比較(等同於strcmp)。char* == char*進行 指針比較(檢查內存地址)。- 混合比較(例如
string == char*)默認為 指針比較。
示例:
impl Point {
fn add(self, other: Point) -> Point {
return Point{x: self.x + other.x, y: self.y + other.y};
}
}
let p3 = p1 + p2; // 調用 p1.add(p2)
多索引示例:
struct Matrix {
data: int[9];
}
impl Matrix {
fn get(self, row: int, col: int) -> int {
return self.data[row * 3 + col];
}
}
let m = Matrix{data: [1,0,0, 0,1,0, 0,0,1]};
let val = m[1, 2]; // 調用 Matrix.get(m, 1, 2)
這些運算符是內置語言特性,不能直接重載。
| 運算符 | 名稱 | 描述 |
|---|---|---|
| ` | >` | 管道 |
?? |
空合併 | 如果 val 為 NULL,val ?? default 返回 default (用於指針) |
??= |
空賦值 | 如果 val 為 NULL 則賦值 |
?. |
安全導航 | 僅當 ptr 不為 NULL 時訪問字段 |
? |
Try 運算符 | 如果存在錯誤則返回 (用於 Result/Option 類型) |
自動解引用:
指針字段訪問 (ptr.field) 和方法調用 (ptr.method()) 會自動解引用指針,等同於 (*ptr).field。
Zen C 提供了多種控制台打印選項,包括關鍵字和簡潔的簡寫形式。
| 關鍵字 | 描述 |
|---|---|
print "text" |
打印到 stdout,不帶尾隨換行符。 |
println "text" |
打印到 stdout,帶尾隨換行符。 |
eprint "text" |
打印到 stderr,不帶尾隨換行符。 |
eprintln "text" |
打印到 stderr,帶尾隨換行符。 |
Zen C 允許直接將字符串字面量用作語句來進行快速打印:
| 語法 | 等效項 | 描述 |
|---|---|---|
"Hello World" |
println "Hello World" |
隱式添加換行符。 |
"Hello World".. |
print "Hello World" |
不帶尾隨換行符。 |
!"Error" |
eprintln "Error" |
輸出到 stderr。 |
!"Error".. |
eprint "Error" |
輸出到 stderr,不帶換行符。 |
你可以使用 {} 語法將表達式直接嵌入到字串字面量中。這適用於所有列印方法和字串簡寫。
Zen C 中的字串插值是隱式的:如果你的字串包含 {...},它將自動被解析為插值字串。你也可以顯式地使用 f 作為前綴(例如 f"..."),以強制執行插值語義。
let x = 42;
let name = "Zen";
println "值: {x}, 名稱: {name}";
"值: {x}, 名稱: {name}"; // 簡寫形式的 println
轉義花括號:使用 {{ 輸出字面量 {,使用 }} 輸出字面量 }:
let json = "JSON: {{\"鍵\": \"值\"}}";
// 輸出: JSON: {"鍵": "值"}
原始字串 (Raw Strings):若要定義完全忽略插值與轉義序列的字串,請在其前加上 r(例如 r"..."):
let regex = r"\w+"; // 包含精確的 \ w +
let raw_json = r'{"key": "value"}'; // 不需要跳脫大括號
Zen C 使用 """ 分隔符支持原始多行字串區塊。這對於編寫嵌入式語言(如 GLSL、HTML)或在 comptime 區塊生成 C 程式碼非常有用,你無需手動轉義換行符與內部引號。
與標準字串一樣,多行字串支持隱式插值。您也可以顯式地加上前綴:
f"""...""": 顯式標記為插值字串塊。r"""...""": 顯式標記為原始字串塊(無插值,無轉義序列)。
let prompt = """
請輸入您的名字:
輸入 "exit" 取消。
""";
let world = "world";
let script = """
fn hello() {
println "hello, {world}!";
}
""";
let pure_raw = r"""
這裡的 {braces} 只是純文本,而 \n 就只是斜線和 n。
""";
Zen C 支持使用 ? 前綴進行用戶輸入提示的簡寫。
? "提示文本": 打印提示信息(不換行)並等待輸入(讀取一行)。? "輸入年齡: " (age): 打印提示並掃描輸入到變量age中。- 格式說明符會根據變量類型自動推斷。
let age: int;
? "你多大了? " (age);
println "你 {age} 歲了。";
Zen C 允許帶有符合人體工程學輔助的手動內存管理。
在當前作用域退出時執行代碼。Defer 語句按照後進先出 (LIFO) 的順序執行。
let f = fopen("file.txt", "r");
defer fclose(f);
Warning
為了防止未定義行為,defer 塊內不允許使用控制流語句(return, break, continue, goto)。
在作用域退出時自動釋放變量。
autofree let types = malloc(1024);
Zen C 將帶有析構函數(如 File, Vec, 或 malloc 的指針)的類型視為 資源。為了防止雙重釋放錯誤,資源不能被隱式複製。
- 默認移動:分配資源變量會轉移所有權。原始變量變得無效(已移動)。
- 複製類型:沒有析構函數的類型可以申請參與
Copy行為,使賦值變成複製。
診斷與哲學: 如果你看到錯誤 "Use of moved value",編譯器是在告訴你:"此類型擁有一個資源(如內存或句柄),盲目複製它是不安全的。"
Note
對比: 與 C/C++ 不同,Zen C 不會隱式複製擁有資源的值。
函數參數: 將值傳遞給函數遵循與賦值相同的規則:資源會被移動,除非通過引用傳遞。
fn process(r: Resource) { ... } // 'r' 被移動進函數
fn peek(r: Resource*) { ... } // 'r' 被借用 (引用)
顯式克隆: 如果你 確實 想要一個資源的兩個副本,請顯式執行:
let b = a.clone(); // 調用 Clone trait 中的 'clone' 方法
選擇性複製 (值類型): 對於沒有析構函數的小型類型:
struct Point { x: int; y: int; }
impl Copy for Point {} // 選擇參與隱式複製
fn main() {
let p1 = Point { x: 1, y: 2 };
let p2 = p1; // 已複製。p1 保持有效。
}
實現 Drop 以自動運行清理邏輯。
impl Drop for MyStruct {
fn drop(self) {
self.free();
}
}
使用 impl 為類型定義方法。
impl Point {
// 靜態方法 (構造函數慣例)
fn new(x: int, y: int) -> Self {
return Point{x: x, y: y};
}
// 實例方法
fn dist(self) -> float {
return sqrt(self.x * self.x + self.y * self.y);
}
}
Self 簡寫: 在具有 self 參數的方法中,可以使用 .欄位 作為 self.欄位 的簡寫:
impl Point {
fn dist(self) -> float {
return sqrt(.x * .x + .y * .y); // 等同於 self.x, self.y
}
}
Zen C 允許你使用相同的 impl 語法在原始類型(如 int、bool 等)上定義方法。
impl int {
fn abs(self) -> int {
return *self < 0 ? -(*self) : *self;
}
}
let x = -10;
let y = x.abs(); // 10
let z = (-5).abs(); // 5 (Literals supported)
定義共享行為。
struct Circle { radius: f32; }
trait Drawable {
fn draw(self);
}
impl Drawable for Circle {
fn draw(self) { ... }
}
let circle = Circle{};
let drawable: Drawable = &circle;
Zen C 包含與語言語法集成的標準 Trait。
Iterable
實現 Iterable<T> 以便為你的自定義類型啟用 for-in 循環。
import "std/iter.zc"
// 定義一個迭代器
struct MyIter {
curr: int;
stop: int;
}
impl MyIter {
fn next(self) -> Option<int> {
if self.curr < self.stop {
self.curr += 1;
return Option<int>::Some(self.curr - 1);
}
return Option<int>::None();
}
}
// 實現 Iterable
impl MyRange {
fn iterator(self) -> MyIter {
return MyIter{curr: self.start, stop: self.end};
}
}
// 在循環中使用
for i in my_range {
println "{i}";
}
Drop
實現 Drop 來定義一個在對象超出範圍時運行的析構函數 (RAII)。
import "std/mem.zc"
struct Resource {
ptr: void*;
}
impl Drop for Resource {
fn drop(self) {
if self.ptr != NULL {
free(self.ptr);
}
}
}
Note
如果一個變量被移動,則原始變量不會調用 drop。它遵循 資源語義。
Copy
標記 Trait,用於選擇支持 Copy 行為(隱式複製)而不是移動語義。通過 @derive(Copy) 使用。
Caution
實現了 Copy 的類型不得定義析構函數 (Drop)。
@derive(Copy)
struct Point { x: int; y: int; }
fn main() {
let p1 = Point{x: 1, y: 2};
let p2 = p1; // 已複製!p1 保持有效。
}
Clone
實現 Clone 以允許顯式複製擁有資源的類型。
import "std/mem.zc"
struct MyBox { val: int; }
impl Clone for MyBox {
fn clone(self) -> MyBox {
return MyBox{val: self.val};
}
}
fn main() {
let b1 = MyBox{val: 42};
let b2 = b1.clone(); // 顯式複製
}
使用 use 嵌入其他結構體。你可以將它們混合進來(展平字段)或者為它們命名(嵌套字段)。
struct Entity { id: int; }
struct Player {
// 混入 (未命名): 展平字段
use Entity; // 直接將 'id' 添加到 Player
name: string;
}
struct Match {
// 組合 (命名): 嵌套字段
use p1: Player; // 通過 match.p1 訪問
use p2: Player; // 通過 match.p2 訪問
}
結構體和函數的類型安全模板。
// 泛型結構體
struct Box<T> {
item: T;
}
// 泛型函數
fn identity<T>(val: T) -> T {
return val;
}
// 多參數泛型
struct Pair<K, V> {
key: K;
value: V;
}
基於 pthreads 構建。
async fn fetch_data() -> string {
// 在後台運行
return "Data";
}
fn main() {
let future = fetch_data();
let result = await future;
}
在編譯時運行程式碼以生成原始碼或列印訊息。
comptime {
// 在編譯時生成程式碼(寫入 stdout)
println "let build_date = \"2024-01-01\";";
}
println "Build Date: {build_date}";
輔助函式
comptime 區塊內可用的特殊函式:
yield(str)- 明確輸出生成的程式碼(printf 的替代方案)compile_error(msg)- 以致命錯誤訊息停止編譯compile_warn(msg)- 發出編譯時警告(允許繼續編譯)
comptime {
compile_warn("正在生成最佳化程式碼...");
let ENABLE_FEATURE = 1;
if (ENABLE_FEATURE == 0) {
compile_error("必須啟用功能!");
}
println "let FEATURE_ENABLED = 1;";
}
構建元數據
在編譯時存取編譯器構建資訊:
__COMPTIME_TARGET__- 平台字串:"linux","windows"或"macos"__COMPTIME_FILE__- 當前正在編譯的原始檔案名稱
comptime {
// 平台特定的程式碼生成
println "let PLATFORM = \"{__COMPTIME_TARGET__}\";";
}
println "運行於: {PLATFORM}";
Tip
在 comptime 字串內使用 {{ 和 }} 來轉義大括號。
將文件嵌入為指定類型。
// 默認 (Slice_char)
let data = embed "assets/logo.png";
// 類型化嵌入
let text = embed "shader.glsl" as string; // 嵌入為 C-string
let rom = embed "bios.bin" as u8[1024]; // 嵌入為固定數組
let wav = embed "sound.wav" as u8[]; // 嵌入為 Slice_u8
導入編譯器插件以擴展語法。
import plugin "regex"
let re = regex! { ^[a-z]+$ };
將預處理器宏傳遞給 C。
Tip
對於簡單的常量,請使用 def。當你需要 C 預處理器宏或條件編譯標誌時,請使用 #define。
#define MAX_BUFFER 1024
使用 @cfg() 根據 -D 標誌有條件地包含或排除任何頂層聲明。
// 編譯: zc build app.zc -DUSE_OPENGL
@cfg(USE_OPENGL)
import "opengl_backend.zc";
@cfg(USE_VULKAN)
import "vulkan_backend.zc";
@cfg(not(USE_OPENGL))
@cfg(not(USE_VULKAN))
fn fallback_init() { println "未選擇後端"; }
| 形式 | 含義 |
|---|---|
@cfg(NAME) |
如果設定了 -DNAME 則包含 |
@cfg(not(NAME)) |
如果未設定 -DNAME 則包含 |
@cfg(any(A, B, ...)) |
如果任意條件為真則包含 (OR) |
@cfg(all(A, B, ...)) |
如果所有條件為真則包含 (AND) |
一個聲明上的多個 @cfg 使用 AND 組合。not() 可以在 any() 和 all() 內部使用。適用於任何頂層聲明:fn、struct、import、impl、raw、def、test 等。
修飾函數和結構體以修改編譯器行為。
| 屬性 | 作用域 | 描述 |
|---|---|---|
@must_use |
函數 | 如果忽略返回值則發出警告。 |
@deprecated("msg") |
函數/結構體 | 使用時發出帶有消息的警告。 |
@inline |
函數 | 提示編譯器進行內聯。 |
@noinline |
函數 | 防止內聯。 |
@packed |
結構體 | 移除字段間的填充。 |
@align(N) |
結構體 | 強制按 N 字节對齊。 |
@constructor |
函數 | 在 main 之前運行。 |
@destructor |
函數 | 在 main 退出後運行。 |
@unused |
函數/變量 | 抑制未使用變量警告。 |
@weak |
函數 | 弱符號鏈接。 |
@section("name") |
函數 | 將代碼放置在特定段中。 |
@noreturn |
函數 | 函數不會返回 (例如 exit)。 |
@pure |
函數 | 函數無副作用 (優化提示)。 |
@cold |
函數 | 函數不太可能被執行 (分支預測提示)。 |
@hot |
函數 | 函數頻繁執行 (優化提示)。 |
@export |
函數/結構體 | 導出符號 (默認可見性)。 |
@global |
函數 | CUDA: 內核入口點 (__global__)。 |
@device |
函數 | CUDA: 設備函數 (__device__)。 |
@host |
函數 | CUDA: 主機函數 (__host__)。 |
@comptime |
函數 | 用於編譯時執行的輔助函數。 |
@cfg(NAME) |
任意 | 條件編譯:僅在傳遞 -DNAME 時包含。支援 not()、any()、all()。 |
@derive(...) |
結構體 | 自動實現 Trait。支持 Debug, Eq (智能派生), Copy, Clone。 |
@ctype("type") |
函數參數 | 覆蓋參數生成的 C 類型。 |
@<custom> |
任意 | 將泛型屬性傳遞給 C (例如 @flatten, @alias("name"))。 |
Zen C 支持強大的 自定義屬性 系統,允許你在代碼中直接使用任何 GCC/Clang 的 __attribute__。任何不被 Zen C 編譯器顯式識別的屬性都會被視為泛型屬性並傳遞給生成的 C 代碼。
這提供了對高級編譯器特性、優化和鏈接器指令的訪問,而無需在語言核心中提供顯式支持。
Zen C 屬性直接映射到 C 屬性:
@name→__attribute__((name))@name(args)→__attribute__((name(args)))@name("string")→__attribute__((name("string")))
Zen C 提供了尊重移動語義的 "智能派生":
@derive(Eq):生成一個通過引用獲取參數的相等性方法 (fn eq(self, other: T*))。- 當比較兩個非 Copy 結構體 (
a == b) 時,編譯器會自動通過引用傳遞b(&b) 以避免移動它。 - 字段上的遞歸相等性檢查也會優先使用指針訪問,以防止所有權轉移。
- 當比較兩個非 Copy 結構體 (
Zen C 為內聯匯編提供了一流支持,直接轉譯為 GCC 風格的擴展 asm。
在 asm 塊內編寫原始匯編。字符串會自動拼接。
asm {
"nop"
"mfence"
}
防止編譯器優化掉具有副作用的匯編代碼。
asm volatile {
"rdtsc"
}
Zen C 通過命名綁定簡化了複雜的 GCC 約束語法。
// 語法: : out(變量) : in(變量) : clobber(寄存器)
// 使用 {變量} 佔位符語法以提高可讀性
fn add_five(x: int) -> int {
let result: int;
asm {
"mov {x}, {result}"
"add $5, {result}"
: out(result)
: in(x)
: clobber("cc")
}
return result;
}
| 類型 | 語法 | GCC 等效項 |
|---|---|---|
| 輸出 | : out(variable) |
"=r"(variable) |
| 輸入 | : in(variable) |
"r"(variable) |
| 破壞 | : clobber("rax") |
"rax" |
| 內存 | : clobber("memory") |
"memory" |
Note
使用 Intel 語法時(通過 -masm=intel),必須確保你的構建配置正確(例如,//> cflags: -masm=intel)。TCC 不支持 Intel 語法的匯編。
Zen C 支持在源文件頂部使用特殊註釋來配置構建過程,無需複雜的構建系統或 Makefile。
| 指令 | 參數 | 描述 |
|---|---|---|
//> link: |
-lfoo 或 path/to/lib.a |
鏈接庫或對象文件。 |
//> lib: |
path/to/libs |
添加庫搜索路徑 (-L)。 |
//> include: |
path/to/headers |
添加包含頭文件搜索路徑 (-I)。 |
//> framework: |
Cocoa |
鏈接 macOS Framework。 |
//> cflags: |
-Wall -O3 |
向 C 編譯器傳遞任意標誌。 |
//> define: |
MACRO 或 KEY=VAL |
定義預處理器宏 (-D)。 |
//> pkg-config: |
gtk+-3.0 |
運行 pkg-config 並追加 --cflags 和 --libs。 |
//> shell: |
command |
在構建期間執行 shell 命令。 |
//> get: |
http://url/file |
如果特定文件不存在,則下載該文件。 |
1. 操作系統守護 (OS Guarding)
在指令前加上操作系統名稱,以使其僅在特定平台上應用。
受支持的前綴:linux:, windows:, macos: (或 darwin:)。
//> linux: link: -lm
//> windows: link: -lws2_32
//> macos: framework: Cocoa
2. 環境變量展開
使用 ${VAR} 語法在指令中展開環境變量。
//> include: ${HOME}/mylib/include
//> lib: ${ZC_ROOT}/std
//> include: ./include
//> lib: ./libs
//> link: -lraylib -lm
//> cflags: -Ofast
//> pkg-config: gtk+-3.0
import "raylib.h"
fn main() { ... }
以下關鍵字在 Zen C 中是保留的。
alias, def, enum, fn, impl, import, let, module, opaque, struct, trait, union, use
async, await, break, catch, continue, defer, else, for, goto, guard, if, loop, match, return, try, unless, while
asm, assert, autofree, comptime, const, embed, launch, ref, sizeof, static, test, volatile
true, false, null
以下標識符是保留的,因為它們是 C11 中的關鍵字:
auto, case, char, default, do, double, extern, float, inline, int, long, register, restrict, short, signed, switch, typedef, unsigned, void, _Atomic, _Bool, _Complex, _Generic, _Imaginary, _Noreturn, _Static_assert, _Thread_local
and, or
Zen C 提供了兩種與 C 代碼交互的方式:信任導入 (Trusted Imports) (方便) 和 顯式 FFI (安全/精確)。
你可以使用 import 關鍵字直接導入 .h 擴展名的 C 頭文件。這會將頭文件視為一個模塊,並假設通過它訪問的所有符號都存在。
//> link: -lm
import "math.h" as c_math;
fn main() {
// 編譯器信任不僅正確;直接生成 'cos(...)'
let x = c_math::cos(3.14159);
}
Note
零樣板代碼。立即訪問頭文件中的所有內容。 缺點: Zen C 不提供類型安全 (錯誤將在稍後由 C 編譯器捕獲)。
對於嚴格的類型檢查,或當你不想包含頭文件文本時,請使用 extern fn.
include <stdio.h> // 在生成的 C 代碼中發出 #include <stdio.h>
// 定義嚴格簽名
extern fn printf(fmt: char*, ...) -> c_int;
fn main() {
printf("Hello FFI: %d\n", 42); // 由 Zen C 進行類型檢查
}
Note
Zen C 確保類型匹配。 缺點: 需要手動聲明函數。
import "file.h": 將頭文件註冊為命名模塊。啟用對符號的隱式訪問 (例如file::function())。include <file.h>: 純粹在生成的 C 代碼中發出#include <file.h>。不向 Zen C 編譯器引入任何符號;必須使用extern fn才能訪問它們。
Zen C 包含一個涵蓋基本功能的標準庫 (std)。
點擊查看所有標準庫模塊
| 模塊 | 描述 | 文檔 |
|---|---|---|
std/bigfloat.zc |
任意精度浮點運算。 | 文檔 |
std/bigint.zc |
任意精度整數 BigInt。 |
文檔 |
std/bits.zc |
底層位運算操作 (rotl, rotr 等)。 |
文檔 |
std/complex.zc |
複數算術 Complex。 |
文檔 |
std/vec.zc |
可增長動態數組 Vec<T>。 |
文檔 |
std/string.zc |
堆分配的 String 類型,支持 UTF-8。 |
文檔 |
std/queue.zc |
先進先出隊列 (環形緩衝區)。 | 文檔 |
std/map.zc |
泛型哈希表 Map<V>。 |
文檔 |
std/fs.zc |
文件系統操作。 | 文檔 |
std/io.zc |
標準輸入/輸出 (print/println)。 |
文檔 |
std/option.zc |
可選值 (Some/None)。 |
文檔 |
std/result.zc |
錯誤處理 (Ok/Err)。 |
文檔 |
std/path.zc |
跨平台路徑操作。 | 文檔 |
std/env.zc |
進程環境變量。 | 文檔 |
std/net/ |
TCP, UDP, HTTP, DNS, URL. | 文檔 |
std/thread.zc |
線程與同步。 | 文檔 |
std/time.zc |
時間測量與睡眠。 | 文檔 |
std/json.zc |
JSON 解析與序列化。 | 文檔 |
std/stack.zc |
後進先出棧 Stack<T>。 |
文檔 |
std/set.zc |
泛型哈希集合 Set<T>。 |
文檔 |
std/process.zc |
進程執行與管理。 | 文檔 |
std/regex.zc |
正則表達式 (基於 TRE)。 | 文檔 |
std/simd.zc |
原生 SIMD 向量類型。 | 文檔 |
Zen C 提供內置的語言服務器 (LSP) 和 REPL 以增強開發體驗。
Zen C 語言服務器 (LSP) 支持標準的 LSP 特性,用於編輯器集成:
- 轉到定義
- 查找引用
- 懸停信息
- 補全 (函數/結構體名,方法/字段的點補全)
- 文檔符號 (大綱)
- 簽名幫助
- 診斷 (語法/語義錯誤)
啟動語言服務器(通常在編輯器的 LSP 設置中配置):
zc lsp它通過標準 I/O (JSON-RPC 2.0) 進行通信。
Read-Eval-Print Loop 允許你交互式地嘗試 Zen C 代碼。
zc repl- 交互式編碼:輸入表達式或語句以立即求值。
- 持久歷史:命令保存在
~/.zprep_history中。 - 啟動腳本:自動加載
~/.zprep_init.zc中的命令。
| 命令 | 描述 |
|---|---|
:help |
顯示可用命令。 |
:reset |
清除當前會話歷史 (變量/函數)。 |
:vars |
顯示活躍變量。 |
:funcs |
顯示用戶定義的函數。 |
:structs |
顯示用戶定義的結構體。 |
:imports |
顯示活躍導入。 |
:history |
顯示會話輸入歷史。 |
:type <expr> |
顯示表達式的類型。 |
:c <stmt> |
顯示語句生成的 C 代碼。 |
:time <expr> |
基准測試表達式 (運行 1000 次迭代)。 |
:edit [n] |
在 $EDITOR 中編輯命令 n (默認:最後一條)。 |
:save <file> |
將當前會話保存到 .zc 文件。 |
:load <file> |
將 .zc 文件加載並執行到會話中。 |
:watch <expr> |
監視表達式 (每次輸入後重新求值)。 |
:unwatch <n> |
移除監視。 |
:undo |
從會話中移除最後一條命令。 |
:delete <n> |
移除索引為 n 的命令。 |
:clear |
清屏。 |
:quit |
退出 REPL。 |
! <cmd> |
運行 shell 命令 (如 !ls)。 |
Zen C 包含一個內建的語言伺服器,用於編輯器整合。
- 安裝與設定指南
- 支援的編輯器: VS Code, Neovim, Vim, Zed, 以及任何支援 LSP 的編輯器。
使用 zc lsp 啟動服務器。
Zen C 程序可以使用標準的 C 調試器(如 LLDB 或 GDB)進行調試。
為了在 VS Code 中獲得最佳體驗,請安裝官方的 Zen C 擴充功能。對於調試,您可以使用 C/C++(由 Microsoft 提供)或 CodeLLDB 擴充功能。
將這些配置添加到您的 .vscode 目錄中,以啟用一鍵調試:
tasks.json (構建任務):
{
"label": "Zen C: Build Debug",
"type": "shell",
"command": "zc",
"args": [ "${file}", "-g", "-o", "${fileDirname}/app", "-O0" ],
"group": { "kind": "build", "isDefault": true }
}launch.json (調試器):
{
"name": "Zen C: Debug (LLDB)",
"type": "lldb",
"request": "launch",
"program": "${fileDirname}/app",
"preLaunchTask": "Zen C: Build Debug"
}Zen C 旨在與大多數 C11 編譯器配合使用。某些特性依賴於 GNU C 擴展,但這些擴展通常在其他編譯器中也能工作。使用 --cc 標誌切換後端。
zc run app.zc --cc clang
zc run app.zc --cc zig點擊查看編譯器支持詳情
| 編譯器 | 通過率 | 受支持特性 | 已知局限性 |
|---|---|---|---|
| GCC | 100% (全面) | 所有特性 | 無. |
| Clang | 100% (全面) | 所有特性 | 無. |
| Zig | 100% (全面) | 所有特性 | 無. 使用 zig cc 作為替代 C 編譯器. |
| TCC | 98% (高) | 結構體, 泛型, Trait, 模式匹配 | 不支持 Intel ASM, 不支持 __attribute__((constructor)). |
Warning
編譯器構建警告: 雖然 Zig CC 作為 Zen C 程序的後端非常出色,但使用它構建 Zen C 編譯器本身可能會通過驗證,但會生成無法通過測試的不穩定二進制文件。我們建議使用 GCC 或 Clang 構建編譯器,並僅將 Zig 用作操作代碼的後端。
Zig 的 zig cc 命令提供了 GCC/Clang 的替代方案,具有出色的跨平台編譯支持。使用 Zig:
# 使用 Zig 編譯並運行 Zen C 程序
zc run app.zc --cc zig
# 使用 Zig 構建 Zen C 編譯器本身
make zigZen C 可以通過 --cpp 標誌生成 C++ 兼容的代碼,從而實現與 C++ 庫的無縫集成。
# 直接使用 g++ 編譯
zc app.zc --cpp
# 或者轉譯用於手動構建
zc transpile app.zc --cpp
g++ out.c my_cpp_lib.o -o app包含 C++ 頭文件並在 raw 塊中使用 C++ 代碼:
include <vector>
include <iostream>
raw {
std::vector<int> make_vec(int a, int b) {
return {a, b};
}
}
fn main() {
let v = make_vec(1, 2);
raw { std::cout << "Size: " << v.size() << std::endl; }
}
Note
--cpp 標誌會將後端切換為 g++ 並發出 C+ 兼容的代碼(使用 auto 代替 __auto_type,使用函數重載代替 _Generic,以及對 void* 進行顯式轉換)。
Zen C 通過轉譯為 CUDA C++ 來支持 GPU 編程。這使你在維持 Zen C 人體工程學語法的同時,能夠利用內核中的強大 C++ 特性(模板、constexpr)。
# 直接使用 nvcc 編譯
zc run app.zc --cuda
# 或者轉譯用於手動構建
zc transpile app.zc --cuda -o app.cu
nvcc app.cu -o app| 屬性 | CUDA 等效項 | 描述 |
|---|---|---|
@global |
__global__ |
內核函數 (運行在 GPU,從主機調用) |
@device |
__device__ |
設備函數 (運行在 GPU,從 GPU 調用) |
@host |
__host__ |
主機函數 (明確僅 CPU 運行) |
Zen C 提供了一個簡潔的 launch 語句用於調用 CUDA 內核:
launch kernel_name(args) with {
grid: num_blocks,
block: threads_per_block,
shared_mem: 1024, // 可選
stream: my_stream // 可選
};
這轉譯為:kernel_name<<<grid, block, shared, stream>>>(args);
使用帶有 @global 的 Zen C 函數語法和 launch 語句:
import "std/cuda.zc"
@global
fn add_kernel(a: float*, b: float*, c: float*, n: int) {
let i = thread_id();
if i < n {
c[i] = a[i] + b[i];
}
}
fn main() {
def N = 1024;
let d_a = cuda_alloc<float>(N);
let d_b = cuda_alloc<float>(N);
let d_c = cuda_alloc<float>(N);
defer cuda_free(d_a);
defer cuda_free(d_b);
defer cuda_free(d_c);
// ... 初始化數據 ...
launch add_kernel(d_a, d_b, d_c, N) with {
grid: (N + 255) / 256,
block: 256
};
cuda_sync();
}
Zen C 為常見的 CUDA 操作提供了一個標準庫,以減少 raw 塊的使用:
import "std/cuda.zc"
// 內存管理
let d_ptr = cuda_alloc<float>(1024);
cuda_copy_to_device(d_ptr, h_ptr, 1024 * sizeof(float));
defer cuda_free(d_ptr);
// 同步
cuda_sync();
// 線程索引 (在內核內部使用)
let i = thread_id(); // 全局索引
let bid = block_id();
let tid = local_id();
Note
注意: --cuda 標誌設置 nvcc 為編譯器並隱含 --cpp 模式。需要安裝 NVIDIA CUDA Toolkit。
當使用相容的後端編譯器(GCC 14+, Clang 14+)時,Zen C 支援現代 C23 特性。
auto: 如果__STDC_VERSION__ >= 202300L,Zen C 會自動將型別推導映射到標準 C23auto。_BitInt(N): 使用iN和uN型別(例如i256,u12,i24)存取 C23 任意位元寬度整數。
Zen C 可以通過 --objc 標誌編譯為 Objective-C (.m),允許你使用 Objective-C 框架(如 Cocoa/Foundation)和語法。
# 使用 clang (或 gcc/gnustep) 編譯
zc app.zc --objc --cc clang使用 include 包含頭文件,並在 raw 塊中使用 Objective-C 語法 (@interface, [...], @"")。
//> macos: framework: Foundation
//> linux: cflags: -fconstant-string-class=NSConstantString -D_NATIVE_OBJC_EXCEPTIONS
//> linux: link: -lgnustep-base -lobjc
include <Foundation/Foundation.h>
fn main() {
raw {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(@"來自 Objective-C 的問候!");
[pool drain];
}
println "Zen C 也能正常工作!";
}
Note
注意: Zen C 字符串插值通過調用 debugDescription 或 description 同樣適用於 Objective-C 對象 (id)。
我們歡迎各類貢獻!無論是修復 Bug、完善文檔,還是提出新功能建議。
請參閱 CONTRIBUTING_ZH_TW.md 了解有關如何貢獻、運行測試和提交拉取請求的詳細指南。
關於安全漏洞報告的說明,請參閱 SECURITY_ZH_TW.md。
本項目使用了第三方庫。完整許可證文本可在 LICENSES/ 目錄中找到。
- cJSON (MIT 許可證):用於語言服務器中的 JSON 解析和生成。
- zc-ape (MIT 許可證):由 Eugene Olonov 開發的原版 Zen-C 實際上便攜的可執行文件 (APE) 端口。
- Cosmopolitan Libc (ISC 許可證):使 APE 成為可能納基礎庫。
- TRE (BSD 許可證):用於標準庫中的正則表達式引擎。
- zenc.vim (MIT 許可證):官方 Vim/Neovim 插件,主要作者為 davidscholberg。