内存memory,用于暂存数据。其中存储的内容会在函数被调用(包括外部函数)时擦除,所以其使用开销相对较小。
存储storage,贮存了合约声明中所有的变量。 虚拟机会为每份合约分别划出一片独立的 存储storage 区域,并在函数相互调用时持久存在,所以其使用开销非常大。
栈stack,用于存放小型的局部变量。使用几乎是免费的,但容量有限。
不同数据类型的变量会有各自默认的存储地点:
状态变量总是会贮存在 存储storage 中
函数参数默认存放在 内存memory 中
结构、数组或映射类型的局部变量,默认会放在 存储storage 中
除结构、数组及映射类型之外的局部变量,会储存在栈中
一个常见误区就是声明了一个局部变量,就认为它会创建在 内存memory 中,其实它会被创建在 存储storage 中:
/// 这份合约包含一处错误
pragma solidity ^0.4.0;
contract C {
uint someVariable;
uint[] data;
function f() public {
uint[] x;
x.push(2);
data = x;
}
}
局部变量 x 的数据类型是 uint[] storage,但由于 存储storage 不是动态分配的,它需要在使用前通过状态变量赋值。所以 x 本身不会被分配 存储storage 的空间,取而代之的是,它只是作为 存储storage 中已有变量的别名。
实际上会发生的是,编译器将 x 解析为一个 存储storage 指针,并默认将指针指向 存储插槽storage slot 0 。这就造成 someVariable (贮存在 存储插槽storage slot 0)会被 x.push(2) 更改。
注:在本例中,两个合约变量 someVariable 和 data 会被预先分配到两个 存储插槽storage slot 中,即 存储插槽storage slot 0 和 存储插槽storage slot 1 。上面的程序会使局部变量 x 变成指向保存了变量 someVariable 的 存储插槽storage slot 0 的指针。
/// 正确的做法
pragma solidity ^0.4.0;
contract C {
uint someVariable;
uint[] data;
function f() public {
uint[] x = data;
x.push(2);
}
}