一、赋值位置
类似js中,深拷贝,浅拷贝。
1.storage状态变量 = storage状态变量,没有修改指针,而是深拷贝。
pragma solidity ^ 0.5.0;
contract Locations {
uint[] public stateVar = [1,2]; //storage
uint[] public stateVar2;
function doSomething() public returns(uint) {
stateVar2 = stateVar;
stateVar = [3,4];
require(stateVar2[0]==3,'equal to 3');
return stateVar2[0]; //returns 1
}
}
2.memory 局部变量=storage 状态变量,没有修改指针,而是深拷贝。
pragma solidity ^ 0.5.0;
contract Locations {
uint[] public stateVar = [1,2]; //storage
function doSomething() public returns(uint) {
uint[] memory localVar; //memory
localVar = stateVar;
stateVar = [3,4];
require(localVar[0]==1,'equal to 3');
return localVar[0]; //1
}
}
3.storage局部变量 = storage 状态变量
引用
pragma solidity ^0.4.0;
contract TestLoc {
uint[] x; // x的存储位置是storage
// memoryArray的存储位置是 memory
function f(uint[] memoryArray) public returns (uint[]) {
x = memoryArray; // 从 memory 复制到 storage
uint[] storage y = x; // storage 引用传递局部变量y(y 是一个 storage 引用)
y[1] = 2; // x y 都会被修改
// 错误, 不能将memory赋值给局部变量
// y = memoryArray;
g(x); // 引用传递, g可以改变x的内容
h(x); // 拷贝到memory, h无法改变x的内容
return x;
}
function g(uint[] storage storageArray) internal {
storageArray[2] = 3;
}
function h(uint[] memory memoryArray) public pure {
memoryArray[2] = 4;
}
}
5.内存变量复制到内存变量
对于引用类型的局部变量,从一个内存变量复制到另一个内存变量不会创建副本。直接修改指针,浅拷贝
memory 局部变量 = memory 局部变量 (直接修改指针,浅拷贝)
对于引用类型的局部变量,从一个内存变量复制到另一个内存变量不会创建副本。
对于值类型的局部变量仍然创建一个新副本。
pragma solidity ^ 0.5.0;
contract Locations {
function doSomething() public pure returns(uint) {
uint[] memory stateVar = new uint[](3); //storage
stateVar[0] = 1;
stateVar[1] = 2;
uint[] memory localVar = stateVar; //storage
stateVar[0] = 3;
require(localVar[0]==1,'equal to 3');
return localVar[0]; //returns 3
}
}
二、函数修饰符 modifier
pragma solidity ^0.5.0;
contract Mutex{
bool locked;
modifier noReentrancy(){
require(!locked, "reetrant call.");
locked = true;
_; // 跳回function
locked = false;
}
function withdraw() public noReentrancy {
(bool success,) = msg.sender.call("");
require(success);
}
}
Inherited
pragma solidity >=0.6.0<0.8.0;
contract Base1{
modifer foo() virtual {_;};
}
contract Base2{
modifer foo() virtual {_;};
}
contract Inherited1 is Base1{
modifer foo() override {_;};
}
contract Inherited2 is Base1, Base2{
modifer foo() override(Base1, Base2) {_;};
}
三、时间Event
1.声明一个事件,按照惯例,事件名称以大写字母开头,以区别于函数。
event Deposit(address indexed _from, bytes32 indexed _id, uint _value);
2.触发事件
emit Deposit(msg.sender, _id, msg.value);
第一个参数用于存储事件签名的哈希值,这样就只剩下三个参数用于索引参数。
pragma solidity ^0.5.0;
contract Counter {
uint256 public count = 0;
event Increment(address who); // 声明事件
function increment() public {
emit Increment(msg.sender); // 触发事件
count += 1;
}
}
3.js中监听
counter = web3.eth.contract(abi).at(address);
counter.Increment(function (err, result) {
if (err) {
return error(err);
}
log("Count was incremented by address: " + result.args.who);
getCount();
});
getCount();
四、特殊函数
0.6版本后出现的2个特殊函数:receive、fallback函数
参考:https://learnblockchain.cn/article/1817
Solidity中的函数为:
function function_name(<param_type> <param_name>) [returns(<return_type>)]{ … }
但是有一种特殊的函数----回退函数,它是合约里的特殊函数,没有名字,不能有参数,没有返回值。当调用的函数找不到时,就会调用默认的fallback函数
FallBack函数的实用场景
场景一:空投
利用FallBack函数,用户只需要使用钱包向空投合约发送0金额的转账(只消耗手续费),空投合约就可以向该地址进行空投。
场景二:锁仓
用户使用钱包将代币转账到锁仓合约中,锁仓合约利用FallBack函数接收到请求,就可以执行锁仓逻辑了。
五、数组
5.1数组声明
uint [3] arr1; // 3是数组长度
uint [] memory arr= new uint[](3); // 3是数组长度,用这种方式,数组长度不能省略
uint [2] memory arr1 = [uint(2),3];
5.2多维数组声明
uint[][5] arr;
5.3数组长度
uint [10] tens; // 不能修改长度
uint[] public b = new uint[](7); // storage类型可以通过length修改长度
memory类型数组不能修改长度、不能push
uint[] memory a = new uint[](7);
// a.push(10);
// a.length = 10;
只有storage不固定长度数组,才可以用length
5.4字符串操作
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8;
import "github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol"; // 引入这个包
contract StringTest {
using strings for *;
string public myStr;
string niceName = unicode"陈小浩同学";
function foo(string memory strPart) public {
myStr = myStr.toSlice().concat(strPart.toSlice());
}
}