Solidity 映射 Mapping
映射类型提供了键到值的数据存取能力。一个映射中,通过提供一个特定的键,可以存取与这个键相对应的值。
在 Solidity 这中,映射以 mapping(键的类型 => 值的类型)
的形式声明。比如, mapping(address => uint)
声明了一个地址向 uint
的映射。映射中,键的类型不可以是动态数组、合约、枚举、结构体或映射本身,其他类型则没有问题;值的类型没有以上限制,可以是包括映射在内的任何类型。此外,与其他语言的类似容器不同,Solidity 的映射不支持遍历,如果需要,可以寻找第三方的库,或手动维护一个数组来实现遍历的可能。
映射只允许作为状态变量,不过它也可以作为 storage
上的引用类型出现在合约的内部函数里。
与其他类型一样,通过用 public
可见性修饰符修饰,Solidity 会自动生成该映射的 getter
函数。最简单的情况下,没有数组和映射的嵌套, getter
函数需要一个参数作为键,返回对应的值。否则, getter
所需的参数会递归地为每一个键和索引而增加。
映射在 storage
中的存储形式
与动态数组类似,映射在 storage 中的存储方式也很特殊。映射 在初始化时也会在 storage 的某位置 $p$ 占有一个槽位。但与动态数组不同的是,此槽位仅仅用来区分不同的映射,操作映射类型并不会用到此处的值。对于映射 $m$ 中每一个键 $k_x$,都可以通过函数 $f(p, k_x) = keccak256(k . p)$ 得到一个 32 字节长的哈希值。此哈希值便是映射 $m$ 中,键 $k_x$ 所对应的值在 storage
中的地址。同数组一样,由于碰撞的可能性实在太小, 因此不会检测此地址是否发生碰撞。
由于这种设计,Solidity 的映射并不能自行实现遍历。 Solidity 的映射没有键是否被被使用的概念,也没有存储"设置"过的键,直接将键所对应的值零散地存放在整个 Storage 中,即使可以遍历其中存放的所有的值,也无法判断那些值属于哪里。不过这个问题技术上并不难解决,声明一个用于记录目标映射的数组,在向映射插入值的同时,把对应的键同时记入该数组中。需要遍历映射的时候,用该数组中所存的键即可。
如何在 geth 中找到 mapping 的存储位置
注意:
- 在数组中,key 就是 key
- mapping 中,pos 是合约存储时数据定义的位置(pos 是存储槽位置)。
因此, sha3(key+pos) 是 mapping 真正的 key。geth 中查找还需要转换为 bigNumber 然后 hex 格式化
> key
"00000000000000000000000046fb9a22689c4a4bfb494baeafbb8b2993725305"
> pos
"0000000000000000000000000000000000000000000000000000000000000001"
> bn=web3.sha3(key + pos, {"encoding":"hex"})
"0x4a6915a70ddb253ab9075c26d94720491095a5a0a6d31c6720a4db10b12f661e"
> sn=web3.toBigNumber(bn)
3.3656819177407030101749625369691302081266253965792325840314023187955028289054e+76
> eth.getStorageAt(con.address,sn)
"0x0000000000000000000000000000000000000000000000000000000000001edc"
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: Solidity 引用类型
下一篇: 彻底找到 Tomcat 启动速度慢的元凶
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论