智慧合約語言 Solidity 教程系列4 - 資料儲存位置分析

Tiny熊發表於2017-12-22

最新內容會更新在主站深入淺出區塊鏈社群
原文連結:智慧合約語言 Solidity 教程系列4 - 資料儲存位置分析

寫在前面

Solidity 是以太坊智慧合約程式語言,閱讀本文前,你應該對以太坊、智慧合約有所瞭解,
如果你還不瞭解,建議你先看以太坊是什麼

這部分的內容官方英文文件講的不是很透,因此我在參考Solidity官方文件(當前最新版本:0.4.20)的同時加入了深入分析部分,歡迎訂閱專欄

資料位置(Data location)

在系列第一篇,我們提到 Solidity 型別分為兩類:
值型別(Value Type)引用型別(Reference Types)
前面我們已經介紹完了值型別,接下來會介紹引用型別。

引用型別是一個複雜型別,佔用的空間通常超過256位, 拷貝時開銷很大,因此我們需要考慮將它們儲存在什麼位置,是memory(記憶體中,資料不是永久存在)還是storage(永久儲存在區塊鏈中)
所有的複雜型別如陣列(arrays)和資料結構(struct)有一個額外的屬性:資料的儲存位置(data location)。可為memorystorage

根據上下文的不同,大多數時候資料位置有預設值,也通過指定關鍵字storage和memory修改它。

函式引數(包含返回的引數)預設是memory
區域性複雜型別變數(local variables)和 狀態變數(state variables) 預設是storage

區域性變數:區域性作用域(越過作用域即不可被訪問,等待被回收)的變數,如函式內的變數。狀態變數:合約內宣告的公有變數

還有一個儲存位置是:calldata,用來儲存函式引數,是隻讀的,不會永久儲存的一個資料位置。外部函式的引數(不包括返回引數)被強制指定為calldata。效果與memory差不多。

資料位置指定非常重要,因為他們影響著賦值行為。
在memory和storage之間或與狀態變數之間相互賦值,總是會建立一個完全獨立的拷貝。
而將一個storage的狀態變數,賦值給一個storage的區域性變數,是通過引用傳遞。所以對於區域性變數的修改,同時修改關聯的狀態變數。
另一方面,將一個memory的引用型別賦值給另一個memory的引用,不會建立拷貝(即:memory之間是引用傳遞)。

  1. 注意:不能將memory賦值給區域性變數。
  2. 對於值型別,總是會進行拷貝。

下面看一段程式碼:

pragma solidity ^0.4.0;

contract C {
    uint[] x; //  x的儲存位置是storage

    // memoryArray的儲存位置是 memory
    function f(uint[] memoryArray) public {
        x = memoryArray;    // 從 memory 複製到 storage
        var y = x;          // storage 引用傳遞區域性變數y(y 是一個 storage 引用)
        y[7];               // 返回第8個元素
        y.length = 2;       // x同樣會被修改
        delete x;           // y同樣會被修改

        // 錯誤, 不能將memory賦值給區域性變數
        // y = memoryArray;  

        // 錯誤,不能通過引用銷燬storage
        // delete y;        

        g(x);               // 引用傳遞, g可以改變x的內容
        h(x);               // 拷貝到memory, h無法改變x的內容
    }

    function g(uint[] storage storageArray) internal {}
    function h(uint[] memoryArray) public {}
}

總結

強制的資料位置(Forced data location)

  • 外部函式(External function)的引數(不包括返回引數)強制為:calldata
  • 狀態變數(State variables)強制為: storage

預設資料位置(Default data location)

  • 函式引數及返回引數:memory
  • 複雜型別的區域性變數:storage

深入分析

storage 儲存結構是在合約建立的時候就確定好了的,它取決於合約所宣告狀態變數。但是內容可以被(交易)呼叫改變。

Solidity 稱這個為狀態改變,這也是合約級變數稱為狀態變數的原因。也可以更好的理解為什麼狀態變數都是storage儲存。

memory 只能用於函式內部,memory 宣告用來告知EVM在執行時建立一塊(固定大小)記憶體區域給變數使用。

storage 在區塊鏈中是用key/value的形式儲存,而memory則表現為位元組陣列

關於棧(stack)

EVM是一個基於棧的語言,棧實際是在記憶體(memory)的一個資料結構,每個棧元素佔為256位,棧最大長度為1024。
值型別的區域性變數是儲存在棧上。

不同儲存的消耗(gas消耗)

  • storage 會永久儲存合約狀態變數,開銷最大
  • memory 僅儲存臨時變數,函式呼叫之後釋放,開銷很小
  • stack 儲存很小的區域性變數,幾乎免費使用,但有數量限制。

參考視訊

我們也推出了目前市面上最全的視訊教程:深入詳解以太坊智慧合約語言Solidity
目前我們也在招募體驗師,可以點選連結瞭解。

參考資料

Solidity官方文件-型別

深入淺出區塊鏈 - 系統學習區塊鏈,打造最好的區塊鏈技術部落格

相關文章