1. 程式人生 > >『0007』- Solidity狀態變數、區域性變數與memory 、storage之間的愛恨情仇

『0007』- Solidity狀態變數、區域性變數與memory 、storage之間的愛恨情仇

在上一節中,我們瞭解了Solidity型別中哪些是值型別,哪些是引用型別,以及值型別引用型別的簡單對比。

本篇教程中,我們將全面講解memory,storage在Solidity開發中的作用,以及值型別引用型別在合約中memory/storage關鍵字的區別。

一段程式碼清楚認識狀態變數、區域性變數

pragma solidity ^0.4.4;

contract Person {

    int public _age;
    string public _name;

    function Person(int age,string name) {
          _age = age;
          _name = name;
    }


    function
f(string name) { var name1 = name; ... } }

在這段程式碼中,_age_name就屬於狀態變數Person(int age,string name)中的agename,還有f(string name)中的name以及f()函式中宣告的name1都預設屬於本地/區域性變數

值型別程式碼演示

pragma solidity ^0.4.4;

contract Person {

    int public _age;

    function Person(int age) {
      _age = age;
    }

    function
f() {
midifyAge(_age); } function midifyAge(int age) { age = 100; } }

在這段程式碼中,在我們建立合約時,因為建構函式中需要傳入一個引數age,如下圖所示,我傳入的值是29

合約建立完後,我們很容易在介面中看到_age的初始值為29

接下來我們切換到f方法,然後點選執行,因為_age是值型別,所以在函式傳參或者將值型別的變數值賦值給一個新變數,當我們嘗試修改新變數時,原來的值型別變數值並不會發生任何變化,在本案例中,當我們呼叫midifyAge(_age)程式碼時,我們可以理解成,建立了一個臨時變數age

,並且將_age的值傳給了age,因為是值傳遞,當我們嘗試在midifyAge函式中修改新變數age的值時,原來的變數值_age的值保持不變。

引用型別memory/storage

引用型別的變數有兩種型別,分別是memorystorage

memory(值傳遞)

pragma solidity ^0.4.4;

contract Person {

    string public  _name;

    function Person() {
        _name = "liyuechun";
    }

    function f() {

        modifyName(_name);
    }

    function modifyName(string name)  {

        var name1 = name;
        bytes(name1)[0] = 'L';
    }
}

程式碼解析

上面的程式碼中:

function modifyName(string name)  {

    var name1 = name;
    bytes(name1)[0] = 'L';
}

等價於:

function modifyName(string memory name)  {

    var name1 = name;
    bytes(name1)[0] = 'L';
}

等價於:

function modifyName(string memory name)  {

    string  memory name1 = name;
    bytes(name1)[0] = 'L';
}

等價於:

function modifyName(string name)  {

    string  memory name1 = name;
    bytes(name1)[0] = 'L';
}

由上面幾種寫法,我們不難看出,當引用型別作為函式引數時,它的型別預設為memory,函式引數為memory型別的變數給一個變數賦值時,這個變數的型別必須和函式引數型別一致,所以我們可以寫成string memory name1 = name;,或者var name1 = name;var宣告一個變數時,這個變數的型別最終由賦給它值的型別決定。

任何函式引數當它的型別為引用型別時,這個函式引數都預設為memory型別,memory型別的變數會臨時拷貝一份值儲存到記憶體中,當我們將這個引數值賦給一個新的變數,並嘗試去修改這個新的變數的值時,最原始的變數的值並不會發生變化。

例如:

在本案例中,當建立合約時,_name的值為liyuechun,當我們呼叫f()函式時,f()函式中會將_name的值賦給臨時的memory變數name,換句話說,因為name的型別為memory,所以name_name會分別指向不同的物件,當我們嘗試去修改name指標指向的值時,_name所指向的內容不會發生變化。

storage(指標傳遞)

當函式引數為memory型別時,相當於值傳遞,而storage型別的函式引數將是指標傳遞。

如果想要在modifyName函式中通過傳遞過來的指標修改_name的值,那麼必須將函式引數的型別顯示設定為storage型別,storage型別拷貝的不是值,而是_name指標,當呼叫modifyName(_name)函式時,相當於同時有_namename,name1三個指標同時指向同一個物件,我們可以通過三個指標中的任何一個指標修改他們共同指向的內容的值。

pragma solidity ^0.4.4;

contract Person {

    string public  _name;

    function Person() {
        _name = "liyuechun";
    }

    function f() {

        modifyName(_name);
    }

    function modifyName(string storage name)  {

        var name1 = name;
        bytes(name1)[0] = 'L';
    }
}

備註:

function modifyName(string storage name)  {

    var name1 = name;
    bytes(name1)[0] = 'L';
}

等價於:

function modifyName(string storage name)  {

    string storage name1 = name;
    bytes(name1)[0] = 'L';
}

接下來我們將上面的程式碼拷貝到Ethereum Wallet中,你會發現有一個地方會報錯。如下圖所示:

報錯的內容為:

 Location has to be memory for publicly visible 
 functions (remove the "storage" keyword).
    function modifyName(string storage name)  {
                        ^-----------------^

報錯原因:因為函式預設為public型別,但是當我們的函式引數如果為storage型別時,函式的型別必須為internal或者private

完整無錯誤程式碼如下:

pragma solidity ^0.4.4;

contract Person {

    string public  _name;

    function Person() {
        _name = "liyuechun";
    }

    function f() {

        modifyName(_name);
    }

    function modifyName(string storage name) internal {

        var name1 = name;
        bytes(name1)[0] = 'L';
    }
}
  • 部署程式碼

小結

本篇文章主要全面講解memory,storage在Solidity開發中的作用,以及值型別引用型別在合約中memory/storage關鍵字的區別。在本篇教程中,我們使用storage關鍵字時,我們同時使用了兩個關鍵字internalprivate,下一篇教程中,春哥將為大家介紹合約中狀態變數函式許可權問題。

技術交流

  • 區塊鏈技術交流QQ群:348924182

  • 「區塊鏈部落」官方公眾號