3.1 變量存儲(chǔ)類型
引用類型變量占空間大,賦值的時(shí)候直接傳遞地址(類似指針),在使用時(shí)必需聲明數(shù)據(jù)存儲(chǔ)位置。
Solidity中引用類型包括:數(shù)組(array)、結(jié)構(gòu)體(struct)、映射(mapping)。
Solidity數(shù)據(jù)存儲(chǔ)類型有三種:storage、memory、calldata。
| 存儲(chǔ)類型 | 存儲(chǔ)位置 | 消耗gas | 使用場(chǎng)景 |
|---|---|---|---|
| storage | 鏈上 | 多 | 狀態(tài)變量 |
| memory | 內(nèi)存 | 少 | 函數(shù)參數(shù)、臨時(shí)變量 |
| calldata | 內(nèi)存 | 少 | 函數(shù)參數(shù)(不可修改) |
calldata一般用于函數(shù)參數(shù),但是是只讀類型,不可修改:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract VarStorage {
// calldata
function fcalldata(uint[] calldata _x) pure external returns (uint) {
// calldata 為只讀類型,不能修改
// _x[0] = 1; // 報(bào)錯(cuò)
return _x[0];
}
}
3.2 賦值規(guī)則
在不同存儲(chǔ)類型相互賦值時(shí)候,有時(shí)會(huì)產(chǎn)生獨(dú)立的副本(修改新變量不會(huì)影響原變量),有時(shí)會(huì)產(chǎn)生引用(修改新變量會(huì)影響原變量)。規(guī)則如下:
| 賦值形式 | 規(guī)則 |
|---|---|
| storage -> storage | 引用 |
| storage -> memory | 副本 |
| memory -> memory | 引用 |
| memory -> storage | 副本 |
- 代碼示例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract V2VRule {
uint[] public x = [1,2,3];
// storage -> storage
function s2s() external {
uint[] storage y = x; // 創(chuàng)建引用
y[0] = 12; // 修改y會(huì)修改x
}
// storage -> memory
function s2m() external view{
uint[] memory y = x; // 創(chuàng)建副本
y[0] = 23; // 修改y不會(huì)修改x
}
// memory -> memory
function m2m() external pure returns (uint[3] memory) {
uint[3] memory x1 = [uint(1),2,3];
uint[3] memory y = x1; // 創(chuàng)建引用
y[0] = 34; // 修改y會(huì)修改x
return x1;
}
// memory -> storage
function m2s() external returns (uint[3] memory) {
uint[3] memory y = [uint(1),2,3];
x = y; // 創(chuàng)建副本
x[0] = 45; // 修改x不會(huì)修改y
return y;
}
}
3.3 變量作用域
3.3.1 狀態(tài)變量
狀態(tài)變量是數(shù)據(jù)存儲(chǔ)在鏈上的變量,所有合約內(nèi)函數(shù)都可以訪問(wèn) ,gas消耗高。狀態(tài)變量在合約內(nèi)、函數(shù)外聲明:
contract Variables {
uint public x = 1;
uint public y;
string public z;
}
3.3.2 局部變量
局部變量是僅在函數(shù)執(zhí)行過(guò)程中有效的變量,函數(shù)退出后,變量無(wú)效。局部變量的數(shù)據(jù)存儲(chǔ)在內(nèi)存里,不上鏈,gas低。局部變量在函數(shù)內(nèi)聲明:
function bar() external pure returns(uint){
uint xx = 1;
uint yy = 3;
uint zz = xx + yy;
return(zz);
}
3.3.3 全局變量
全局變量是全局范圍工作的變量,都是solidity預(yù)留關(guān)鍵字。他們可以在函數(shù)內(nèi)不聲明直接使用:
function global() external view returns(address, uint, bytes memory){
address sender = msg.sender;
uint blockNum = block.number;
bytes memory data = msg.data;
return(sender, blockNum, data);
}
在上面例子里,我們使用了3個(gè)常用的全局變量:msg.sender, block.number和msg.data,他們分別代表請(qǐng)求發(fā)起地址,當(dāng)前區(qū)塊高度,和請(qǐng)求數(shù)據(jù)。下面是一些常用的全局變量,更完整的列表請(qǐng)看這個(gè)鏈接:
| 全局變量 | 類型 | 說(shuō)明 |
|---|---|---|
| blockhash(uint blockNumber) | bytes32 | 給定區(qū)塊的哈希值 – 只適用于256最近區(qū)塊, 不包含當(dāng)前區(qū)塊。 |
| block.coinbase | address payable | 當(dāng)前區(qū)塊礦工的地址 |
| block.gaslimit | uint | 當(dāng)前區(qū)塊的gaslimit |
| block.number | uint | 當(dāng)前區(qū)塊的number |
| block. timestamp | uint | 當(dāng)前區(qū)塊的時(shí)間戳,為unix紀(jì)元以來(lái)的秒 |
| gasleft() | uint256 | 剩余 gas |
| msg. data | bytes calldata | 完整call data |
| msg. sender | address payable | 消息發(fā)送者 (當(dāng)前 caller) |
| msg. sig | bytes4 | calldata的前四個(gè)字節(jié) (function identifier) |
| msg. value | uint | 當(dāng)前交易發(fā)送的wei值 |