原文:《Solidity 極簡入門: 4. 變量數據存儲和作用域storage/memory/calldata》
作者:0xAA
我最近在重新學solidity,鞏固一下細節,也寫一個「Solidity 極簡入門」,供小白們使用(編程大佬可以另找教程),每週更新1-3 講。
所有代碼開源在:github.com/AmazingAng/WTFSolidity
Solidity 中的引用類型
引用類型(Reference Type):包括數組(array),結構體(struct)和映射(mapping),這類變量佔空間大,賦值時候直接傳遞地址(類似指針)。由於這類變量比較複雜,佔用存儲空間大,我們在使用時必須要聲明數據存儲的位置。
數據位置
solidity 數據存儲位置有三類:storage,memory 和calldata。不同存儲位置的gas 成本不同。 storage 類型的數據存在鏈上,類似計算機的硬盤,消耗gas 多;memory 和calldata 類型的臨時存在內存裡,消耗gas 少。大致用法:
1.storage:合約裡的狀態變量默認都是storage,存儲在鏈上。
2.memory:函數里的參數和臨時變量一般用memory,存儲在內存中,不上鍊。
3.calldata:和memory 類似,存儲在內存中,不上鍊。與memory 的不同點在於calldata 變量不能修改(immutable),一般用於函數的參數。例子:
不同類型相互賦值時的規則
在不同存儲類型相互賦值時候,有時會產生獨立的副本(修改新變量不會影響原變量),有時會產生引用(修改新變量會影響原變量)。規則如下:
1.storage(合約的狀態變量)賦值給本地storage(函數里的)時候,會創建引用,改變新變量會影響原變量。例子:
1.storage 賦值給memory,會創建獨立的複本,修改其中一個不會影響另一個;反之亦然。例子:
1.memory 賦值給memory,會創建引用,改變新變量會影響原變量。
2.其他情況,變量賦值給storage,會創建獨立的複本,修改其中一個不會影響另一個。
變量的作用域
Solidity 中變量按作用域劃分有三種,分別是狀態變量(state variable),局部變量(local variable)和全局變量(global variable)
1. 狀態變量
狀態變量是數據存儲在鏈上的變量,所有合約內函數都可以訪問,gas 消耗高。狀態變量在合約內、函數外聲明:
我們可以在函數里更改狀態變量的值:
2. 局部變量
局部變量是僅在函數執行過程中有效的變量,函數退出後,變量無效。局部變量的數據存儲在內存裡,不上鍊,gas 低。局部變量在函數內聲明:
3. 全局變量
全局變量是全局範圍工作的變量,都是solidity 預留關鍵字。他們可以在函數內不聲明直接使用:
在上面例子裡,我們使用了3 個常用的全局變量:msg.sender, block.number 和msg.data,他們分別代表請求發起地址,當前區塊高度,和請求數據。下面是一些常用的全局變量,更完整的列表請看這個鏈接:
· blockhash(uint blockNumber): (bytes32) 給定區塊的哈希值– 只適用於256 最近區塊, 不包含當前區塊。
· block.coinbase: (address payable) 當前區塊礦工的地址
· block.gaslimit: (uint) 當前區塊的gaslimit
· block.number: (uint) 當前區塊的number
· block.timestamp: (uint) 當前區塊的時間戳,為unix 紀元以來的秒
· gasleft(): (uint256) 剩餘gas
· msg.data: (bytes calldata) 完整call data
· msg.sender: (address payable) 消息發送者(當前caller)
· msg.sig: (bytes4) calldata 的前四個字節(function identifier)
· msg.value: (uint) 當前交易發送的wei 值
· now : (uint) 當前塊的時間戳
總結
在第4 講,我們介紹了solidity 中的引用類型,數據位置和變量的作用域。重點是storage, memory 和calldata 三個關鍵字的用法。他們出現的原因是為了節省鏈上有限的存儲空間和降低gas。下一講我們會介紹引用類型中的數組。