Solidity語言學習筆記————40、儲存中狀態變數的佈局

FLy_鵬程萬里發表於2018-07-08

儲存中狀態變數的佈局(Layout of State Variables in Storage)

靜態尺寸大小的變數(除了對映和動態尺寸大小的陣列型別(的其他型別變數))在儲存中,是從位置0連續儲存。如果可能的話,不足32個位元組的多個條目被緊湊排列在一個單一的儲存塊,參見以下規則:

  • 在儲存塊中的第一項是儲存低階對齊的。
  • 基本型別只使用了正好儲存它們的位元組數。
  • 如果一個基本型別不適合儲存塊的剩餘部分,則移動到下一個儲存塊中。
  • 結構和陣列的資料總是開始一個新的塊並且佔整個塊(根據這些規則,結構或陣列項都是緊湊排列的)。
警告
當使用小於32位元組的元素時,合約的gas使用可能會更高。這是因為EVM一次執行32個位元組。因此,如果元素小於該元素,則EVM必須使用更多的操作,以便將元素的大小從32位元組減小到所需的大小。
只有在處理儲存值時,使用縮減大小的引數才是有益的,因為編譯器將多個元素打包到一個儲存槽中,因此,將多個讀或寫組合到單個操作中。在處理函式引數或記憶體值時,沒有固有的好處,因為編譯器不打包這些值。
最後,為了允許EVM對此進行優化,確保您嘗試對儲存變數和struct成員進行排序,以便它們可以被緊密打包。例如,以uint128uint128uint256而不是uint128uint256uint128的順序宣告儲存變數,前者只佔用兩個儲存槽,而後者將佔用三個儲存槽。

結構和陣列元素是一個接著一個儲存排列的,就如當初它們被宣告的次序。

由於無法預知的分配的大小,對映和動態尺寸大小的陣列型別(這兩種型別)是使用sha3 計算來找到新的起始位置,來存放值或者陣列資料。這些起始位置總是滿棧塊。

根據上述規則,對映或動態陣列本身存放在(沒有填滿)的儲存塊位置p(或從對映到對映或陣列遞回應用此規則)。對於一個動態陣列,儲存塊儲存了陣列元素的數目(位元組陣列和字串是一個例外,見下文)。對於對映,儲存塊是未使用的(但它是需要的,因此,前後相鄰的兩個相同的對映,將使用一個不同的hash分佈)。陣列資料位於keccak256(p), 對應於一個對映key值k位於keccak256(k . p)(這裡 . 是連線符)。如果該值又是一個非基本型別,位置的偏移量是keccak256(k . p)

如果bytes 和 string是短型別的,它們將和其長度儲存在同一個儲存塊裡。特別是:如果資料最長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.

相關文章