Solidity中的数据存储位置
你会发现这块内容是最具有挑战性的。下面的表格会是每块数据存储位置的概览,标识了读写的权限。具体每个数据存储位置的详情,可以阅读对应的具体模块。
数据读写权限
我们什么时候使用关键字, storage、memory、calldata ?
任何具有复杂类型的变量比如array,struct,mappings或者enum必须指定一个数据存储位置。
Storage
Storage对应合约内的存储。
你可以把你不想被cloned的变量作为Storage存储。
Storage是长期有效的,但是成本比较高。
Memory
在ETH中memory保存了临时变量,就像函数的参数。
而且,memory变量在外部函数调用时会被清除。
Memory是短暂其便宜的!
Calldata
calldata几乎是免费的,但是有一个长度的限制。
Stack
stack存储了小的局部变量。然而它只能存储数量有限的变量。stack在eth中只能有最大1024个元素。
一旦函数执行完成就结束。
函数参数的数据存储位置规则
下面的表格展示了函数参数可选的数据类型,取决于函数的可见性。
外部函数或者公有函数不能访问storage
函数内部的数据存储位置
在函数内部所有的三种数据存储位置都能被指定,不管函数的可见性。然后在在赋值的时候存在一些具体的规则,如下面表格中展示:
Storage
storage references can be assigned a: | |
---|---|
state variable directly | ✅ |
state variable via storage reference | ✅ |
memory reference | ❌ |
calldata value directly | ❌ |
calldata value via calldata reference | ❌ |
Memory
memory references can be assigned a: | |
---|---|
state variable directly | ✅ |
state variable via storage reference | ✅ |
memoryreference | ✅ |
calldata value directly | ✅ |
calldata value via calldata reference | ✅ |
Calldata
calldata references can be assigned a: | |
---|---|
state variable directly | ❌ |
state variable via storagereference | ❌ |
memoryreference | ❌ |
calldata value directly | ✅ |
calldata value via calldatareference | ✅ |
数据存储位置-行为
当我们指定数据存储位置的时候,有2件主要的事情需要考虑:影响范围和gas的使用量。
让我们用一个简单的合约来更好的了解这个区别。为什么比较不用的存储位置,我们使用了不同的函数使用不同的数据类型关键词。
- a getter storage
- a getter memory
- a setter storage
- a setter memory
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
contract Garage {
struct Item {
uint256 units;
}
mapping(uint256 => Item) items;
// gas (view) 24025
function getItemUnitsStorage(uint _itemIndex) public view returns(uint) {
Item storage item = items[_itemIndex];
return item.units;
}
// gas (view) 24055
function getItemUnitsMemory(uint _itemIndex) public view returns (uint) {
Item memory item = items[_itemIndex];
return item.units;
}
}
## github仓库
<https://github.com/coffiasd/solidity-learn>