Skip to content

[Day-5]Zig:賦值(Assignment)與運算子(Operator)

發佈

賦值(Assignment)與運算子(Operator)是各種程式語言中最基本的操作。

賦值

在 Zig 中只有兩種賦值關鍵字:

型別在後。無論是 const 還是 var 都要求一定要在宣告時給值。如果初始值未知或想留空,明確地賦予 undefined,這是 Zig 的「沒有隱藏」的宗旨。

pub fn main() !void {
    // var a: u8;           // 這行會導致編譯錯誤
    var a: u8 = undefined;  // 這是對的
 
    a = 100;
    std.debug.print("Value: {d}\n", .{a});
}

不像 Rust 會明確區分編譯期常數(const)和執行期不可變變數(let),Zig 只使用 const 表達不可變的值。至於它究竟是在執行期還是編譯期確認,Zig 會盡可能將其在編譯期處理完成(如果可能的話)。

自動型別推斷

Zig 擁有自動型別推斷,可以省略型別標記,但僅限於 constcomptime var,一般的 var 必須明確指定型別。

pub fn main() !void {
    const a = 100;
    std.debug.print("Value: {d}\n", .{a});
}

全域變數

在 Zig 中更精準的名稱可能是模組級變數(和常數),但我還是以 C 的習慣稱其全域變數。定義在函式以外的 constvar 會成為全域變數,所有在此模組內的函式都可以直接存取。

只有 const 可以加上 pub 關鍵字,使其成為公開常數,讓其它模組 @import 時可以使用,就和 pub fn 是一樣的。var 不行加上 pub,Zig 不允許公開的變數。

static 區域變數

在 C 中有時會使用 static 區域變數,讓該變數的生命週期變成整個程式,但一樣只有該函式可以存取它(因為是區域的)。Zig 沒有相似的設計,如果需要變數數值在離開函式後依舊保持,直接使用全域變數,其生命週期會延續到整個模組。

強制轉型

有時會需要明確進行強制轉型。使用內建函式 @as() 達成。

const std = @import("std");
 
pub fn main() !void {
    const a: u8 = 5;
    const b = @as(u16, a);
    std.debug.print("Type a: {}\n", .{@TypeOf(a)});
    std.debug.print("Type b: {}\n", .{@TypeOf(b)});
}
Type a: u8
Type b: u16

在此例中,就算不用 @as(),直接打 const b: u16 = a; 也是可以通過編譯並正確執行,因為有 Integer Widening。

運算子

基本

Zig 的基本運算子和多數語言一樣,這裡僅列出一些常用的。

一般的運算後賦值(如 +=*=<<=)也都有。

但是要注意 Zig 的 ++ 是陣列串聯,不是 C 的遞增算子。

還有雖然取址(Address of)和 C 一樣是 &T,但是指標解參考(Dereference、取值)是 T.*,和 C 不太一樣。

const std = @import("std");
 
pub fn main() void {
    const a: u8 = 100;
    const b: u8 = 2;
    const sum: u8 = a + b;
 
    std.debug.print("Sum: {}", .{sum});
}
Sum: 102

溢位

Zig 對於運算的要求相當嚴格,如果運算會發生溢位(Overflow),則必須指定溢位發生時的處理方式是飽和還是繞回,否則引發編譯期錯誤。

  • 繞回運算:數值溢位後,會從最低值繞回。例如 u8 的範圍是 0~255,如果 255 + 1 的話結果就會繞回到 0,255 + 2 就會繞回到 1.
  • 飽和運算:數值溢位後、會保持在最大/小值。例如 u8 的範圍是 0~255,如果 255 + a,只要 a>0,那結果也會一直保持在 u8 的最大值255。

所以 Zig 除了上面的一般運算子外,還分別有 Wrapping 和 Saturating 運算的變體。繞回運算的變體是加上 %;飽和運算的變體是加上 |。例如繞回加法是 a +% b、飽和加法是 a +| b;繞回加法賦值是 a +%= b、飽和加法賦值是 a +|= b

const std = @import("std");
 
pub fn main() void {
    // u8: 0~255
    const a: u8 = 255;
    const b: u8 = 2;
 
    // const nor: u8 = a + b; // Error: overflow
    const wrp: u8 = a +% b;   // Wrapping addition
    const sat: u8 = a +| b;   // Saturating addition
 
    std.debug.print("Wrapping:   {}\n", .{wrp});
    std.debug.print("Saturating: {}\n", .{sat});
}
Wrapping:   1
Saturating: 255

參考

本文以 Zig 0.13.0 為主。並同時發佈在:


[Day-6]Zig:型別(Types)
[Day-4]Zig:函式(Functions)

留言可能不會立即顯示。若過了幾天仍未出現,請 Email 聯繫:)