1. 程式人生 > >solidity學習筆記(5)—— Storage(引用傳遞)和Memory(值傳遞)

solidity學習筆記(5)—— Storage(引用傳遞)和Memory(值傳遞)

Solidity中有兩種型別:值型別和引用型別 Solidity是靜態型別的語言,有值型別和引用型別的區別。

如果一個變數是值型別,那麼當把它的值傳給另一個變數時,是複製值,對新變數的操作不會影響原來的變數;如果該變數是引用型別,那麼當它傳值給另一個變數時,則是把該值的地址複製給新的變數。這樣操作新變數也會導致舊變數的改變。

值型別: 布林型別(bool)、整型(int)、地址型別(address)、定長位元組陣列(bytes)、列舉型別(enums)、函式型別(function);

如果一個變數是值型別,給它賦值時永遠是值傳遞!

引用型別: 字串(string)、陣列(array)、結構體(structs)、字典(mapping)、不定長位元組陣列(bytes)

如果一個變數是引用型別,給它賦值時可以是值,也可以是引用,這決定於該變數是Storage型別還是Memory型別。

關鍵字:Storage 和 Memory

Storage 是把變數永久儲存在區塊鏈中,Memory 則是把變數臨時放在記憶體中,當外部函式對某合約呼叫完成時,記憶體型變數即被移除。 你可以把它想象成儲存在你電腦的硬碟或是RAM中資料的關係。

大多數時候你都用不到這些關鍵字,預設情況下 Solidity 會自動處理它們。

狀態變數(在函式之外宣告的變數)預設為“儲存”形式,並永久寫入區塊鏈;而在函式內部宣告的變數是“記憶體”型的,它們函式呼叫結束後消失。

通過指定引用型別變數的關鍵字,可以人為設定變數為storage或memory。

函式的引用型別引數是storage時,是引用傳遞;函式的引用型別引數是Memory時,是值傳遞;函式值型別引數永遠是值傳遞。

contract example {
  uint _a;    // 狀態變數_a,值型別變數,不存在memory的定位,就是存在區塊鏈中的
  string _b;  // 狀態變數_b,引用型別變數,預設storage
 
  function example1 (string b1){
    _b = b1;  // 引數預設是Memory型變數,進行值傳遞
  }
    
  function example2 (string storage b2) private/internal {
    _b = b2;  // 引數預設是Memory型變數,進行值傳遞;但是這裡設定為storage,就是進行引用傳遞了
              // 引數是storage型別時,函式必須是private或internal型別的
  }
    
  function example3 (uint storage/memory a) {
    _a = a;  // error: storage location can only be given for Array or String type
  }  
}

有一個很好的例子:  

contract SandwichFactory {
  struct Sandwich {
    string name;
    string status;
  }
  Sandwich[] sandwiches;
  function eatSandwich(uint _index) public {
    // Sandwich mySandwich = sandwiches[_index];
    /*
       看上去很直接,不過 Solidity 將會給出警告,告訴你應該明確在這裡定義 `storage` 或者 `memory`。
       所以你應該明確定義 `storage`:
    */
    Sandwich storage mySandwich = sandwiches[_index];
    // 這樣 `mySandwich` 是指向 `sandwiches[_index]`的指標在儲存裡,另外...
    mySandwich.status = "Eaten!";
    // 這將永久把 `sandwiches[_index]` 變為區塊鏈上的儲存,如果你只想要一個副本,可以使用`memory`:
    Sandwich memory anotherSandwich = sandwiches[_index + 1];
    // 這樣 `anotherSandwich` 就僅僅是一個記憶體裡的副本了
    // 另外
    anotherSandwich.status = "Eaten!";
    // 將僅僅修改臨時變數,對 `sandwiches[_index + 1]` 沒有任何影響
    // 不過你可以這樣做:
    sandwiches[_index + 1] = anotherSandwich;
    // 如果你想把副本的改動儲存回區塊鏈儲存
  }
}