跳转至

变量

变量声明 var

使用 var 关键字声明变量。

和绝大多数编程语言类似,直接:

var 变量名 = 初始化表达式;
var a = 1;

即可完成变量的声明与初始化,变量的类型会被自动设置为右侧表达式的类型。


也可以在声明时指定类型:

var 变量名 as 类型;
var 变量名 as 类型 = 初始化表达式;
var a as int;
var b as int = 1;

声明多个变量属于不同的类型:

var a as int = 1, b as float = 2;

声明多个变量属于同一个类型:

var a, b, c as int;
var a, b as int, c, d as float;

此时不能初始化。


声明指向不可变内存的不可变指针:

val a as *int = 0x12345678;
a = null; // error: 无法将 null 赋值给不可变指针
a[0] = 1; // error: 无法修改不可变内存

声明指向可变内存的不可变指针:

val a as *var int = 0x12345678;
a = null; // error: 无法将 null 赋值给不可变指针
a[0] = 1; // success

非空指针:

var a as &int = 0x12345678;
var b as &int = null; // error: 无法将 null 赋值给非空指针

唯一引用:

var a as !int = 0x12345678;
var b as !int = null; // error: 无法将 null 赋值给唯一引用

类型值的断言:

var a as int ($ > 0) = 1;
var b as int ($ > 0) = 0; // error: 无法将 0 赋值给 int($ > 0)

此处 $ 代表当前值,每次写入时都会进行断言。


默认的规则

对于一个变量,如果没有进行任何形式的取地址,那么它绝对不会被外部意外修改。

var a = 1;
while (a > 0) {
  // Do nothing
}

这应当是一个死循环。


允许的优化

对于一个变量,取到的值可以是:

  • 编译时能够确定的值(非 volatile
    warning: 对于编译期可确定的值使用 lit
  • 上次修改后缓存的值(非 volatile
  • 对应内存地址当前的值

不可变变量声明 val

使用 val 关键字声明不可变变量,使用方法与 var 相同。

val 变量名 = 初始化表达式;
val a = 1;

常量设计上在整个生命周期中不可变,但实际上可以强行修改,但进行修改时可能会因编译器的优化策略导致不可预测的结果。

val a = 1;     // 定义常量
println(a);    // 输出 1
*(&a as *int) = 2; // 强行修改为 2
println(a);    // 由于编译器的优化策略,可能输出 1 或 2

这么做的见一个打一个

允许的优化

对于一个常量,取到的值可以是:

  • 编译时能够确定的值(非 volatile
    warning: 对于编译期可确定的值使用 lit
  • 作用域内任意位置缓存的值(非 volatile
  • 对应内存地址当前的值

初始化

Lumos 允许的初始化方式有:

  • 赋值初始化 int my_var = 1;
  • 构造函数初始化 int my_var(1);
    对于基本数据类型有伪构造函数
  • 结构体元素赋值初始化 var my_var as MyStructure = {1, 2, .third = 3};
  • 数组元素赋值初始化 var my_var[] as int = {1, 2, [2] = 3};
  • 默认初始化 var my_var as int;
    默认初始化会将基本数据类型变量初始化为二进制 0,对于其它数据类型则调用默认构造函数 我们推荐显式初始化

默认初始化使得所有变量都会被初始化,即使没有给定初始化表达式。

val a as int; // 初始化为 0
float b; // 初始化为 0.0
bool  c; // 初始化为 false
char  d; // 初始化为 '\0'
str   e; // 初始化为 ""

变量类型与表达式类型不同时变量初始化被视为显式类型转换。但之后的赋值只允许隐式类型转换。

int* a = 0x123456;

延迟初始化

使用 lateinit 作为初始值来让变量不自动初始化。
注意访问未初始化的变量是未定义行为

var a as int = lateinit; // 此时 a 未初始化
a = 1; // 手动初始化 a
println(a);    // 1

对于不可变变量,使用 lateinit 时仅可以赋值一次。

val a as int = lateinit; // 此时 a 未初始化
a = 1;                   // 手动初始化 a
println(a);              // 1
a = 2;                   // error: 无法重新赋值给不可变变量
val a, b, c as int = lateinit;
if (xxx) {
  a = 1;
  b = 2;
  c = 3;
} else {
  a = 4;
  b = 5;
  c = 6;
}

懒初始化

使用 lateinit 接代码块作为初始值来让变量在第一次访问时自动初始化。
代码块将延迟到第一次访问时执行,且只会执行一次。
注意这种情况下不能连续声明多个变量

var a as int = lateinit {
  return 1;
} /* 此处语句已结束,不能接着声明变量 */
println(a); // 此时 a 被初始化为 1

代码块中可以使用外部的变量,但注意变量的值为代码块执行时的值,而非声明时的值。

var a as int = 1;
var b as int = lateinit {
  return a;
}
a = 2;
println(b); // 输出 2
a = 3;
println(b); // 输出 2

全局不可变变量

你不应该声明一个全局的不可变变量,应该用 let(表达式) 或 lit(常量表达式) 代替。

表达式

限定符

restrict

注意 restrict 不是属性

restrict 限定符用于指针,表示指针所指向的内存区域不会被其他指针访问。

int* restrict a = malloc(4);
int* restrict b = a; // 这是不可以的
int*          c = a; // 这也是不可以的

volatile

注意 volatile 不是属性

volatile 限定符用于变量,表示变量可能会被其它线程或硬件改变,编译器不会对其访问(读写)进行优化。

val a as volatile int = 1;

属性

register

@register(寄存器名) 属性用于强制变量存储在寄存器中,而不是内存中。
这会导致相应寄存器无法被其它变量使用
无特殊需求不应该使用

@register("rax")
var a as int = 1;

注意此处的 rax 填写 eax ax al 都是可以的,实际宽度由类型决定。

isrestrict 运算符

isrestrict 运算符用于判断两个指针是否独立。

int* a = malloc(4);
int* b = malloc(4);
int* c = a;

// 当编译器可以自动推断内存块大小时
println(isrestrict(a, b)); // 输出 true
println(isrestrict(a, c)); // 输出 false
// 当编译器不能自动推断内存块大小时
println(isrestrict(a, 4, b, 4)); // 输出 true
println(isrestrict(a, 4, c, 4)); // 输出 false

getter/setter

你可以使用 getter 和 setter 来访问变量。

var a by int {
  \get { // 只能有一个 \get 但可以有多个 \set
    return 1;
  }
  \set { // 不写参数默认为 value
    println(`set a to $value`);
  }
}

a = 2;      // 输出 set a to 2
println(a); // 输出 1

可以不声明类型:

var a by {
  \get -> int {
    return 1;
  }
  \set(value as int) {
    println(`set a to $value`);
  }
}

可以在内部声明变量来存储值:

var a by int {
  var real_a as int = 1;
  \get {
    return real_a;
  }
  \set {
    println(`set a to $value`);
    real_a = value;
  }
}

a = 2;      // 输出 set a to 2
println(a); // 输出 2

外部无法访问到 real_a 变量。