solidity學習筆記(5)—— Storage(引用傳遞)和Memory(值傳遞)
阿新 • • 發佈:2018-12-15
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;
// 如果你想把副本的改動儲存回區塊鏈儲存
}
}