智能合約從入門到精通:Solidity語法之內存變量的佈局和狀態變量的存儲模型

簡介:在前面我們已經講過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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章