2. 变量、类型和范围

学习局部变量、整数作用域、bool 和作用域。

Wave Foundation

按名称处理值

如果您直接在多个位置写下价格,则更改价格时必须找到所有位置。变量是一个存储空间,它为值提供名称,并允许您使用这些名称读取和更改它们。在本章中,您将了解声明、赋值、类型的范围以及块内可见名称的范围。

下面的程序是单独的main.wave的每个部分。保存一个示例,将其运行为wavec run main.wave,并将其替换为下一个示例。

声明和初始化

fun main() {
    var price: i32 = 1200;
    var quantity: i32 = 3;
    println("price={}", price);
    println("quantity={}", quantity);
    println("total={}", price * quantity);
}

执行结果:

price=1200
quantity=3
total=3600

阅读该声明的四个部分。

部分 这个例子 角色
声明关键字 var 创建局部变量
姓名 price 稍后使用的标识符
类型 i32 要存储的值的类型和范围
初始值 1200 要存储的第一个值

类型前的冒号和初始值前的等号具有不同的作用。让你的名字有意义。在此示例中,price 是单价,quantity 是数量。即使相同的i32互换使用,也可能在没有语法错误的情况下做出错误的计算。

分配不是维持关系的公式。

如果将计算结果保存在变量中,则将输入该点的值。它不记住计算并在以后自动重新评估它们。

fun main() {
    var price: i32 = 1200;
    var quantity: i32 = 3;
    var total: i32 = price * quantity;
    quantity = 5;
    println("before recalculation={}", total);
    total = price * quantity;
    println("after recalculation={}", total);
}

执行结果:

before recalculation=3600
after recalculation=6000

quantity = 5 将新值写入已存在的变量。它必须与像var quantity这样的重新声明区分开来。再次替换之前total也是3600。如果程序必须维护多个变量之间的关系,则应编写该程序以在关系发生变化时执行计算。

根据前一个值计算下一个值

先计算赋值语句右侧,并将结果写入左侧存储空间。与数学方程不同,count = count + 1 是有效的更新。

fun main() {
    var count: i32 = 0;
    count = count + 1;
    count += 2;
    count *= 3;
    println("{}", count);
}

执行结果:

9

数值的级数为0 → 1 → 3 → 9。 +=、*= 一起表示计算和存储。如果你写下计算顺序,你就可以发现在哪个阶段,当结果与你的预期不同时,你的想法有所不同。

整数类型的宽度和符号

如果以i开头,则变为signed,如果以u开头,则变为unsigned。最后一个数字是位数。随着位数的增加,可表达的范围增大,存储空间也增大。

类型 最小值 最大值 使用示例
i8 -128 127 小符号值
u8 0 255 一个字节
i16 -32768 32767 小整数数据
u16 0 65535 端口/16 位字段
i32 -2147483648 2147483647 常见小整数计算
u32 0 4294967295 32位位域

Wave 还提供 64、128、256、512 和 1024 位的有符号和无符号整数。更宽的类型并不能使每次计算都安全:结果仍然可能超出所选范围。首先选择您需要的范围。 isz 和usz 遵循目标地址宽度。

必须区分在小类型中存储大文字和通过执行cast有意丢弃位。仅仅为了消除错误而转换为较小的类型可能会改变值本身。下一章将介绍转换。

浮点类型和布尔值

f32·f64 是浮点数。与整数不同,它们可以表示小数部分,但不能准确存储所有小数。这就是为什么金额以小整数单位进行管理的原因之一。

bool代表真与假。您可以通过保存比较结果来为您的条件命名,如下所示:

fun main() {
    var balance: i32 = 5000;
    var cost: i32 = 3600;
    var can_buy: bool = balance >= cost;
    if (can_buy) {
        println("purchase allowed");
    } else {
        println("not enough money");
    }
}

执行结果:

purchase allowed

can_buy 不会自动跟随 balance 和 cost 中的更改。两个值交换后,如果需要当前状态,则再次进行比较。

未初始化的存储空间

var value: i32;是只声明存储空间的形式。读取之前必须写入有效值。不要仅仅因为声明了 0 就认为它会自动插入。在入门课程中,如果在声明的同时立即知道并初始化该值,则更容易理解。

在接收值作为输出参数的库调用中,有时会先声明一个空格,然后在成功时读取该空格。此时,您应该检查该函数的成功结果。避免调用失败后读取未初始化输出的错误。

块和名称的有效范围

块是用大括号括起来的代码区域。如果您在内部声明一个同名的新变量,则该新变量将在该块内使用。这称为shadowing。

fun main() {
    var value: i32 = 10;

    if (true) {
        var value: i32 = value + 5;
        println("inner={}", value);
        value += 1;
        println("inner changed={}", value);
    }

    println("outer={}", value);
}

执行结果:

inner=15
inner changed=16
outer=10

内部声明中的初始表达式value + 5读取外部value。新变量初始化完成后,内部value为15。即使将内部值改为16,外部存储空间也不会改变。过了街区,你会再次在外面看到value。

相反,如果在内部块中只执行value += 1而不执行var,则现有的可见变量将被更改。查看关键字以确定它是新声明还是对现有值的更改。

局部变量和顶级存储

在函数之外,您可以使用const和static。 const代表常量值,static是执行期间维护的存储空间。它们不能像局部变量一样在任何地方声明。

const LIMIT: i32 = 3;
static visits: i32 = 0;

fun visit() {
    visits += 1;
    println("visit={}", visits);
}

fun main() {
    visit();
    visit();
    println("limit={}", LIMIT);
}

执行结果:

visit=1
visit=2
limit=3

即使visit被调用两次,static也不会在每次调用时重置为0。另一方面,如果您在函数内将其声明为 var visits: i32 = 0;,则每次调用它时都会初始化本地存储。共享可变状态可能会使行为难以跟踪,因此首先考虑是否可以通过函数的输入和输出来解决它。

常见错误

  • 当声明和赋值混淆并且不必要地再次声明相同的名称时。
  • 如果您认为存储计算结果的变量会自动跟随输入变量的变化。
  • 如果你认为既然类型相同,那么个数、字节数等单位也相同。
  • 在未初始化的情况下读取或在函数失败时读取输出参数时。
  • 如果您认为局部变量的名称在块外可见。

当查找错误的名称时,请检查名称的声明位置和大括号范围。

练习:计算库存变化

初始库存为 20 件,分两次出售,每次 3 件。打印剩余库存和销售总量。每当库存发生变化时,相同的变量都会更新,并且销量也会单独累积。

完整解答

fun main() {
    var stock: i32 = 20;
    var sold: i32 = 0;
    var order: i32 = 3;
    stock -= order;
    sold += order;
    stock -= order;
    sold += order;
    println("stock={} sold={}", stock, sold);
}

执行结果:

stock=14 sold=6

库存和销量必须一起变化。更新其中任何一个都会破坏值之间的关系。我们将通过学习函数和循环语句来对重复的订单处理进行分组。

整数和浮点类型

整数类型如下:

  • 签名:i8、i16、i32、i64、i128、i256、i512、i1024
  • 无符号:u8、u16、u32、u64、u128、u256、u512、u1024
  • 地址大小整数:isz、usz
  • 浮点:f32、f64

isz是与地址大小匹配的有符号整数类型,usz是与地址大小匹配的无符号整数类型。

其他内置类型

类型 使用
bool true 或 false
char 无符号 8 位字符值。不是任意 Unicode 代码点类型
byte 8 位字节值
str 以 NUL 结尾的字符串字节
ptr<T> 指针定位 T
array<T, N> 元素类型为 T 且长度为 N 的固定长度数组

用户定义的结构、枚举和类型别名也可以在类型位置中使用。

var是声明局部变量的语法。类型别名是一种语法,它用适合代码上下文的名称来表达相同的类型。