天天看點

智能合約從入門到精通:Solidity文法之記憶體變量的布局和狀态變量的存儲模型狀态變量的存儲模型(Layout of State Variables in Storage)

簡介:在前面我們已經講過Solidity語言的一些文法内容,在矩陣元JUICE開放平台的JIDE開發時,一定要注意Layout in Memory和Layout of State Variables in Storage,即記憶體變量的布局和狀态變量的存儲模型。

記憶體變量的布局(Layout in Memory)

Solidity預留了3個32位元組大小的槽位:

  • 0-64:哈希方法的暫存空間(scratch space)
  • 64-96:目前已配置設定記憶體大小(也稱空閑記憶體指針(free memory pointer))

暫存空間可在語句之間使用(如在内聯編譯時使用)

Solidity總是在空閑記憶體指針所在位置建立一個新對象,且對應的記憶體永遠不會被釋放(也許未來會改變這種做法)。

有一些在Solidity中的操作需要超過64位元組的臨時空間,這樣就會超過預留的暫存空間。他們就将會配置設定到空閑記憶體指針所在的地方,但由于他們自身的特點,生命周期相對較短,且指針本身不能更新,記憶體也許會,也許不會被清零(zerod out)。是以,大家不應該認為空閑的記憶體一定已經是清零(zeroed out)的。

狀态變量的存儲模型(Layout of State Variables in Storage)

大小固定的變量(除了映射,變長數組以外的所有類型)在存儲(storage)中是依次連續從位置0開始排列的。如果多個變量占用的大小少于32位元組,會盡可能的打包到單個storage槽位裡,具體規則如下:

· 在storage槽中第一項是按低位對齊存儲(lower-order aligned)(譯者注:意味着是大端序了,因為是按書寫順序。)。

· 基本類型存儲時僅占用其實際需要的位元組。

· 如果基本類型不能放入某個槽位餘下的空間,它将被放入下一個槽位。

· 結構體和數組總是使用一個全新的槽位,并占用整個槽(但在結構體内或數組内的每個項仍遵從上述規則)

優化建議

當使用的元素占用少于32位元組,你的合約的gas使用也許更高。這是因為EVM每次操作32位元組。是以,如果元素比這小,EVM需要更多操作來從32位元組減少到需要的大小。

因為編譯器會将多個元素打包到一個storage槽位,這樣就可以将多次讀或寫組合進一次操作中,隻有在這時,通過縮減變量大小來優化存儲結構才有意義。當操作函數參數和memory的變量時,因為編譯器不會這樣優化,是以沒有上述的意義。

最後,為了友善EVM進行優化,嘗試有意識排序storage的變量和結構體的成員,進而讓他們能打包得更緊密。比如,按這樣的順序定義,uint128, uint128, uint256,而不是uint128, uint256, uint128。因為後一種會占用三個槽位。

非固定大小

結構體和數組裡的元素按它們給定的順序存儲。

由于它們不可預知的大小。映射和變長數組類型,使用Keccak-256哈希運算來找真正資料存儲的起始位置。這些起始位置往往是完整的堆棧槽。

映射和動态數組根據上述規則在位置p占用一個未滿的槽位(對映射裡嵌套映射,數組中嵌套數組的情況則遞歸應用上述規則)。對一個動态數組,位置p這個槽位存儲數組的元素個數(位元組數組和字元串例外,見下文)。而對于映射,這個槽位沒有填充任何資料(但這是必要的,因為兩個挨着的映射将會得到不同的哈希值)。數組的原始資料位置是keccak256(p);而映射類型的某個鍵k,它的資料存儲位置則是keccak256(k . p),其中的.表示連接配接符号。如果定位到的值以是一個非基本類型,則繼續運用上述規則,是基于keccak256(k . p)的新的偏移offset。

bytes和string占用的位元組大小如果足夠小,會把其自身長度和原始資料存在目前的槽位。具體來說,如果資料最多31位長,高位存資料(左對齊),低位存儲長度lenght * 2。如果再長一點,主槽位就隻存lenght * 2 + 1。原始資料按普通規則存儲在keccak256(slot)

是以對于接下來的代碼片段:

pragma solidity ^0.4.4;

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

按上面的代碼來看,結構體從位置0開始,這裡定義了一個結構體,但并沒有對應的結構體變量,故不占用空間。uint x實際是uint256,單個占32位元組,占用槽位0,是以映射data将從槽位1開始。

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

有人在這裡嘗試直接讀取區塊鍊的存儲值,https://github.com/ethereum/solidity/issues/1550

參考内容:https://open.juzix.net/doc

智能合約開發教程視訊:區塊鍊系列視訊課程之智能合約簡介

繼續閱讀