『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)
中的age
和name
,還有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
引用型別的變數有兩種型別,分別是memory
和storage
。
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)
函式時,相當於同時有_name
,name
,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
關鍵字時,我們同時使用了兩個關鍵字internal
和private
,下一篇教程中,春哥將為大家介紹合約中狀態變數和函式的許可權問題。
技術交流
區塊鏈技術交流QQ群:348924182
「區塊鏈部落」官方公眾號