最新內(nèi)容會(huì)更新在主站深入淺出區(qū)塊鏈社區(qū)
原文鏈接:智能合約語(yǔ)言 Solidity 教程系列4 - 數(shù)據(jù)存儲(chǔ)位置分析
Solidity教程系列第4篇 - Solidity數(shù)據(jù)位置分析。
寫在前面
Solidity 是以太坊智能合約編程語(yǔ)言,閱讀本文前,你應(yīng)該對(duì)以太坊、智能合約有所了解,
如果你還不了解,建議你先看以太坊是什么
這部分的內(nèi)容官方英文文檔講的不是很透,因此我在參考Solidity官方文檔(當(dāng)前最新版本:0.4.20)的同時(shí)加入了深入分析部分,歡迎訂閱專欄。
數(shù)據(jù)位置(Data location)
在系列第一篇,我們提到 Solidity 類型分為兩類:
值類型(Value Type) 及 引用類型(Reference Types),
前面我們已經(jīng)介紹完了值類型,接下來會(huì)介紹引用類型。
引用類型是一個(gè)復(fù)雜類型,占用的空間通常超過256位, 拷貝時(shí)開銷很大,因此我們需要考慮將它們存儲(chǔ)在什么位置,是memory(內(nèi)存中,數(shù)據(jù)不是永久存在)還是storage(永久存儲(chǔ)在區(qū)塊鏈中)
所有的復(fù)雜類型如數(shù)組(arrays)和數(shù)據(jù)結(jié)構(gòu)(struct)有一個(gè)額外的屬性:數(shù)據(jù)的存儲(chǔ)位置(data location)。可為memory和storage。
根據(jù)上下文的不同,大多數(shù)時(shí)候數(shù)據(jù)位置有默認(rèn)值,也通過指定關(guān)鍵字storage和memory修改它。
函數(shù)參數(shù)(包含返回的參數(shù))默認(rèn)是memory。
局部復(fù)雜類型變量(local variables)和 狀態(tài)變量(state variables) 默認(rèn)是storage。
局部變量:局部作用域(越過作用域即不可被訪問,等待被回收)的變量,如函數(shù)內(nèi)的變量。狀態(tài)變量:合約內(nèi)聲明的公有變量
還有一個(gè)存儲(chǔ)位置是:calldata,用來存儲(chǔ)函數(shù)參數(shù),是只讀的,不會(huì)永久存儲(chǔ)的一個(gè)數(shù)據(jù)位置。外部函數(shù)的參數(shù)(不包括返回參數(shù))被強(qiáng)制指定為calldata。效果與memory差不多。
數(shù)據(jù)位置指定非常重要,因?yàn)樗麄冇绊懼x值行為。
在memory和storage之間或與狀態(tài)變量之間相互賦值,總是會(huì)創(chuàng)建一個(gè)完全獨(dú)立的拷貝。
而將一個(gè)storage的狀態(tài)變量,賦值給一個(gè)storage的局部變量,是通過引用傳遞。所以對(duì)于局部變量的修改,同時(shí)修改關(guān)聯(lián)的狀態(tài)變量。
另一方面,將一個(gè)memory的引用類型賦值給另一個(gè)memory的引用,不會(huì)創(chuàng)建拷貝(即:memory之間是引用傳遞)。
- 注意:不能將memory賦值給局部變量。
- 對(duì)于值類型,總是會(huì)進(jìn)行拷貝。
下面看一段代碼:
pragma solidity ^0.4.0;
contract C {
uint[] x; // x的存儲(chǔ)位置是storage
// memoryArray的存儲(chǔ)位置是 memory
function f(uint[] memoryArray) public {
x = memoryArray; // 從 memory 復(fù)制到 storage
var y = x; // storage 引用傳遞局部變量y(y 是一個(gè) storage 引用)
y[7]; // 返回第8個(gè)元素
y.length = 2; // x同樣會(huì)被修改
delete x; // y同樣會(huì)被修改
// 錯(cuò)誤, 不能將memory賦值給局部變量
// y = memoryArray;
// 錯(cuò)誤,不能通過引用銷毀storage
// delete y;
g(x); // 引用傳遞, g可以改變x的內(nèi)容
h(x); // 拷貝到memory, h無法改變x的內(nèi)容
}
function g(uint[] storage storageArray) internal {}
function h(uint[] memoryArray) public {}
}
總結(jié)
強(qiáng)制的數(shù)據(jù)位置(Forced data location)
- 外部函數(shù)(External function)的參數(shù)(不包括返回參數(shù))強(qiáng)制為:calldata
- 狀態(tài)變量(State variables)強(qiáng)制為: storage
默認(rèn)數(shù)據(jù)位置(Default data location)
- 函數(shù)參數(shù)及返回參數(shù):memory
- 復(fù)雜類型的局部變量:storage
深入分析
storage 存儲(chǔ)結(jié)構(gòu)是在合約創(chuàng)建的時(shí)候就確定好了的,它取決于合約所聲明狀態(tài)變量。但是內(nèi)容可以被(交易)調(diào)用改變。
Solidity 稱這個(gè)為狀態(tài)改變,這也是合約級(jí)變量稱為狀態(tài)變量的原因。也可以更好的理解為什么狀態(tài)變量都是storage存儲(chǔ)。
memory 只能用于函數(shù)內(nèi)部,memory 聲明用來告知EVM在運(yùn)行時(shí)創(chuàng)建一塊(固定大小)內(nèi)存區(qū)域給變量使用。
storage 在區(qū)塊鏈中是用key/value的形式存儲(chǔ),而memory則表現(xiàn)為字節(jié)數(shù)組
關(guān)于棧(stack)
EVM是一個(gè)基于棧的語(yǔ)言,棧實(shí)際是在內(nèi)存(memory)的一個(gè)數(shù)據(jù)結(jié)構(gòu),每個(gè)棧元素占為256位,棧最大長(zhǎng)度為1024。
值類型的局部變量是存儲(chǔ)在棧上。
不同存儲(chǔ)的消耗(gas消耗)
- storage 會(huì)永久保存合約狀態(tài)變量,開銷最大
- memory 僅保存臨時(shí)變量,函數(shù)調(diào)用之后釋放,開銷很小
- stack 保存很小的局部變量,幾乎免費(fèi)使用,但有數(shù)量限制。
參考視頻
我們也推出了目前市面上最全的視頻教程:深入詳解以太坊智能合約語(yǔ)言Solidity
目前我們也在招募體驗(yàn)師,可以點(diǎn)擊鏈接了解。
參考資料
深入淺出區(qū)塊鏈 - 系統(tǒng)學(xué)習(xí)區(qū)塊鏈,打造最好的區(qū)塊鏈技術(shù)博客