深入理解Solidity——存储中状态变量的布局

存储中状态变量的布局(Layout of State Variables in Storage)

静态尺寸大小的变量(除了映射和动态尺寸大小的数组类型(的其他类型变量))在存储中,是从位置0连续存储。如果可能的话,不足32个字节的多个条目被紧凑排列在一个单一的存储块,参见以下规则:

  • 在存储块中的第一项是存储低阶对齐的。
  • 基本类型只使用了正好存储它们的字节数。
  • 如果一个基本类型不适合存储块的剩余部分,则移动到下一个存储块中。
  • 结构和数组的数据总是开始一个新的块并且占整个块(根据这些规则,结构或数组项都是紧凑排列的)。
警告
当使用小于32字节的元素时,合约的gas使用可能会更高。这是因为EVM一次运行32个字节。因此,如果元素小于该元素,则EVM必须使用更多的操作,以便将元素的大小从32字节减小到所需的大小。
只有在处理存储值时,使用缩减大小的参数才是有益的,因为编译器将多个元素打包到一个存储槽中,因此,将多个读或写组合到单个操作中。在处理函数参数或内存值时,没有固有的好处,因为编译器不打包这些值。
最后,为了允许EVM对此进行优化,确保您尝试对存储变量和struct成员进行排序,以便它们可以被紧密打包。例如,以uint128, uint128, uint256而不是uint128, uint256, uint128的顺序声明存储变量,前者只占用两个存储槽,而后者将占用三个存储槽。

结构和数组元素是一个接着一个存储排列的,就如当初它们被声明的次序。

由于无法预知的分配的大小,映射和动态尺寸大小的数组类型(这两种类型)是使用sha3 计算来找到新的起始位置,来存放值或者数组数据。这些起始位置总是满栈块。

根据上述规则,映射或动态数组本身存放在(没有填满)的存储块位置p(或从映射到映射或数组递归应用此规则)。对于一个动态数组,存储块存储了数组元素的数目(字节数组和字符串是一个例外,见下文)。对于映射,存储块是未使用的(但它是需要的,因此,前后相邻的两个相同的映射,将使用一个不同的hash分布)。数组数据位于keccak256(p), 对应于一个映射key值k位于keccak256(k . p)(这里 . 是连接符)。如果该值又是一个非基本类型,位置的偏移量是keccak256(k . p)

如果bytesstring是短类型的,它们将和其长度存储在同一个存储块里。特别是:如果数据最长31字节,它被存储在高阶字节(左对齐), 低字节存储length * 2。 如果是长类型,主存储块存储 length * 2 + 1, 数据存储在keccak256(slot)

因此,本合约片段如下:

pragma solidity ^0.4.0;

contract C {
  struct s { uint a; uint b; }
  uint x;
  mapping(uint => mapping(uint => s)) data;
}

data[4][9].b 的位置在 keccak256(uint256(9) . keccak256(uint256(4) . uint256(1))) + 1.

上一篇:深入理解Solidity——独立汇编

下一篇:深入理解Solidity——内存布局

你可能感兴趣的:(Solidity文档翻译系列,以太坊去中心化应用开发)