solidity數據位置-memory,storage和calldata


 有三種類型,memory,storage和calldata,一般只有外部函數的參數(不包括返回參數)被強制指定為calldata。這種數據位置是只讀的,不會持久化到區塊鏈

storage存儲或memory內存
memory存儲位置同我們普通程序的內存類似,即分配,即使用,動態分配,越過作用域即不可被訪問,等待被回收。
而對於storage的變量,數據將永遠存在於區塊鏈上。

總結¶
強制指定的數據位置:
    •    外部函數的參數(不包括返回參數): calldata,效果跟 memory 差不多
    •    狀態變量: storage
默認數據位置:
    •    函數參數(包括返回參數): memory
    •    所有其它局部變量: storage

 下面舉例說明賦值行為:

1.memory = storage (值傳遞,互不影響)

pragma solidity ^0.4.24;

contract Person {

    int public _age;

    constructor (int age) public {
      _age = age;
    }

    function f() public view{
      modifyAge(_age);
    }

    function modifyAge(int age) public pure{
      age = 100;
    }
}

 

 

在這里一開始deploy合約時,傳入的age值為30,此時_age的值為30

然后運行f()函數,在這里使用了為storage類型的_age作為函數modifyAge的參數,相當於創建了一個臨時變量age(memory類型),將storage類型的變量_age賦值給memory類型的變量age,是值傳遞,所以在modifyAge函數中,age變量的值的變化並不會影響到_age變量的值

所以再查看_age的值,還是為30

 

 2.storage = memory

當storage是狀態變量(即全局變量時),為值傳遞

當storage為局部變量時,該賦值會出錯,解決方法是將storage的局部變量聲明為memory即可

1)當storage為局部變量時:

如下面的例子:

pragma solidity ^0.4.24;

contract Person {

    string public  _name;

    constructor() public {
        _name = "liyuechun";
    }

    function f() public view{

        modifyName(_name);
    }

    function modifyName(string name) public pure{

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

調用f()函數,將storage類型的狀態變量_name作為參數賦值給函數modifyName(string) memory類型的name形參,為memory = storage,為值傳遞

然后在函數modifyName(string)中,還將memory類型的name形參賦值給memory類型的name1局部變量,memory = memory,為引用傳遞,改變一個另一個也跟着改變,但是因為先是進行了值傳遞,name與_name之間已經互不影響了,所以不會跟着改變_name

 

2)當storage為狀態變量時:

 

pragma solidity ^0.4.24;

contract Person {

    string public  _name;
    string public changedName;

    constructor() public {
        _name = "liyuechun";
    }

    function f() public{//不能在聲明為view

        modifyName(_name);
    }

    function modifyName(string name) public{//不能在聲明為view

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

 

warning:function declared as view,but this expression(potentially) modifies the state and thus requires non-payable(the default) or payable.

因為函數modifyName(string)改變了值changedName的狀態,所以不能聲明為view了

 

調用f()函數,將storage類型的狀態變量_name作為參數賦值給函數modifyName(string) memory類型的name形參,為memory = storage,為值傳遞

然后memory類型的name形參賦值給storage類型的狀態變量changedName,storage = memory,為值傳遞,因此name的值的改變不會導致changedName的值的改變,更不要說_name了

調用f()后為:

 

3.storage = storage

是引用傳遞,所以一個值的變化一定會導致另一個值的變化

pragma solidity ^0.4.24;

contract Person {

    string public  _name;

    constructor() public {
        _name = "liyuechun";
    }

    function f() public{

        modifyName(_name);
    }

    function modifyName(string storage name) internal {

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

⚠️:如果modifyName(string)函數不聲明為internal會報錯:

TypeError:Location has to be memory for publicly visible functions(remove the "storage" keyword)

這是因為形參是默認為memory類型的,這里聲明為storage,那么函數的類型就必須聲明為internal或者private

調用f()函數,首先會將為storage類型的_name變量賦值給modifyName(string)函數storage類型的name形參,storage = storage,為引用傳遞

然后在modifyName(string)函數中,將storage類型的name變量賦值給storage類型的name1變量,storage = storage,為引用傳遞

都為引用傳遞,所以最后name1值的變化會導致_name的值的變化

調用f()后:

其實在這里如果將modifyName(string)函數改成如下,也是能夠成功的,因為其實沒必要進行兩次引用傳遞:

    function modifyName(string storage name) internal {

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

 

4.memory = memory

是引用傳遞,所以一個值的變化一定會導致另一個值的變化

pragma solidity ^0.4.24;

contract Person {

    function modifyName(string name) public pure returns(string){

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

這里調用modifyName(string)函數,將memory類型的形參賦值給memory類型的局部變量name1,memory = memory,為引用傳遞

這時候改變name1的值,從return 的name可以看到,它的值也隨之改變

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM