Solidity 是一门静态类型语言,编译器需要知道任何变量的准确类型。此外,Solidity 也是门注重类型安全的语言,除了整数类型或定点小数类型之间不会有数据截取可能的类型转换之外,其他类型间允许的类型转换都必须显式地进行。
按照变量的作用域,Solidity 分为两类:状态变量(state variable)和局部变量(local variable)。
按照变量的传递方式和特殊性,Solidity 将变量的类型分为三类: 值类型(value type)、引用类型(reference type)和映射(mapping)。
声明变量
声明变量的形式为:
变量类型 变量名;
例如:
// 声明一个名为 count,类型为 uint32 的变量
uint32 count;
声明而未赋值时,变量的初始值为零值,即这个变量的值在内存空间中所有位都为 0。
Solidity 支持声明时赋值,其形式为:
变量类型 变量名 = 表达式;
例如:
// 声明一个名为 count,类型为 uint32 的变量,初值为 16
uint32 count = 16;
// 声明一个名为 time,类型为 uint64 的变量,将已有的变量 now 显式转换的结果作为初值
// 可以看出,初始化赋值不能省略显式转换
uint64 time = uint64(now);
// 声明一个名为 ok,类型为 bool 的变量,将等号后表达式求值的结果作为初值
bool ok = isThisOk() && isThatOk();
// 已经被遗弃的使用 var 关键词自动推导类型的声明
// 这里给出仅仅是为了方便理解以前遗留的代码
// 声明一个名为 x的变量,类型根据函数 f 的返回类型由编译器自动推导。
var x = f();
// Solidity 目前不支持在同一语句中声明多个变量,因此以下语句无法通过编译
// int a, b
状态变量和局部变量
正如之前所说,Solidity 将变量依照作用域分为两类:状态变量和局部变量。
声明在合约之内,函数之外的变量是状态变量,任何相同合约内的函数都可以访问这些状态变量。除非状态变量初始化赋值时依赖到其他的状态变量,状态变量的位置和顺序对程序不会有实质影响。合约外部无法直接访问状态变量,借助 public
可见性修饰符,Solidity 会创建与变量同名的 getter 函数供外部获得此状态变量(直接声明与状态变量同名的函数,将导致代码无法编译)。例如:
pragma solidity ^0.4.0;
contract A {
// 声明一个名为 x,类型为 int 的变量
// 省去可见性修饰符相当于被 private 可见性修饰符修饰
// 即,此语句等同于 int private x;
int x;
// 声明一个名为 y,类型为 int,且被 public 可见性修饰符修饰的变量,将 x+1 作为初值
// 可以看出,状态变量的可见性修饰符紧挨在声明的类型之后
// 由于 x 没有被赋值过,x 的值为零值, y 最终初始化为 1(类型为 int)
int public y = x+1;
constructor() public {
}
}
contract B {
A a = new A();
function foo() public constant returns(int) {
// 编译不通过,因为 a 中的 x 为外部不可见
// int x = a.x();
// 通过 y 的 getter 函数可以获得 a 中 y 的值
int y = a.y();
// 编译不通过,因为 a 中 y 的值只能通过 getter 函数获得
//int y = a.y;
return y;
}
}
声明在函数之内的变量是局部变量。编写本书时,Solidity 尚不支持块级作用域。也就是说,无论变量在何处声明,此变量与其他函数内的声明都处在同一作用域:
function test() public constant returns(int) {
if (false) {
// 不满足条件,不会执行
int a = 100;
}
// 编译不通过,标识符 a 已经声明过了
// int a = 200;
// b 为 10,编译器认为声明了变量 a,调用函数时初始化 a 为其类型的零值
// 之后因为不满足条件,没有执行为其赋值的语句
int b = a + 10;
return b;
}