Skip to content

Latest commit

 

History

History
238 lines (186 loc) · 6.4 KB

File metadata and controls

238 lines (186 loc) · 6.4 KB

7. 映射类型 mapping

在映射中,人們可以透過鍵( Key )來查詢對應的值( Value ),例如:透過一個人的 id 來查詢他的錢包位址。

聲明映射的格式為 mapping(_KeyType => _ValueType) ,其中 _KeyType 和 _ValueType 分別是 Key 和 Value 的變數類型。例子:

mapping(uint => address) public idToAddress; // id映射到地址
mapping(address => address) public swapPair; // 币对的映射,地址到地址

映射的規則:

  • 規則1:

    映射的 _KeyType 只能選擇Solidity內建的值類型,例如 uint , address 等,不能用自訂的結構體。而 _ValueType 可以使用自訂的類型。下面這個例子會報錯,因為 _KeyType 使用了我們自訂的結構體:

      struct Student{
          uint256 id;
          uint256 score; 
      }
      mapping(Student => uint) public testVar;
    
      mapping(uint => address) public idToAddress;
      mapping(address => address) public swapPair;
    
      function writeMap (uint _Key, address _Value) public{
            idToAddress[_Key] = _Value;
      }
    
      // 初始值
        🕵️ 0x0000000000000000000000000000000000000000
    
  • 映射的原理

    • 原理1: 映射不储存任何键(Key)的资讯,也没有length的资讯。

    • 原理2: 映射使用keccak256(abi.encodePacked(key, slot))当成offset存取value,其中slot是映射变量定义所在的插槽位置。

    • 原理3: 因为Ethereum会定义所有未使用的空间为0,所以未赋值(Value)的键(Key)初始值都是各个type的默认值,如uint的默认值是0。

8. 变量初始值

所有值的初始值如下:

bool public _bool; // false
string public _string; // ""
int public _int; // 0
uint public _uint; // 0
address public _address; // 0x0000000000000000000000000000000000000000

enum ActionSet { Buy, Hold, Sell}
ActionSet public _enum; // 第1个内容Buy的索引0

function fi() internal{} // internal空白函数
function fe() external{} // external空白函数

// Reference Types
uint[8] public _staticArray; // 所有成员设为其默认值的静态数组[0,0,0,0,0,0,0,0]
uint[] public _dynamicArray; // `[]`
mapping(uint => address) public _mapping; // 所有元素都为其默认值的mapping
// 所有成员设为其默认值的结构体 0, 0
struct Student{
    uint256 id;
    uint256 score;
}
Student public student;

// delete操作符
bool public _bool2 = true;
function d() external {
    delete _bool2; // delete 会让_bool2变为默认值,false
}

bytes1 // 0x00

9. 常數constant和immutable

constant(常數)和 immutable(不變量)。 狀態變數宣告這兩個關鍵字之後,在初始化後不能更改數值。

這樣做的好處是提升合約的安全性並節省gas。

constant

// constant 变量必须在声明的时候初始化,之后不能改变
uint256 constant CONSTANT_NUM = 10;
string constant CONSTANT_STRING = "0xAA";
bytes constant CONSTANT_BYTES = "WTF";
address constant CONSTANT_ADDRESS = 0x0000000000000000000000000000000000000000;

immutable

// immutable 变量可以在constructor里初始化,之后不能改变
uint256 public immutable IMMUTABLE_NUM = 9999999999;
address public immutable IMMUTABLE_ADDRESS;
uint256 public immutable IMMUTABLE_BLOCK;
uint256 public immutable IMMUTABLE_TEST;

constructor() {
    IMMUTABLE_ADDRESS = address(this);
    IMMUTABLE_NUM = 1118;
    IMMUTABLE_TEST = test();
}

只有數值變數可以宣告 constant 和 immutable;string 和 bytes 可以宣告為constant,但不能為immutable。

下列哪一个变量不适合用 constant 或 immutable 来修饰?

答案:合約的ETH餘額 看到這題有點遲疑後面想想的確是 哈哈

10. 控制流,用Solidity實現插入排序

控制 Solidity的控制流程與其他語言類似,主要包含以下幾種:

if-else

function ifElseTest(uint256 _number) public pure returns(bool){
    if(_number == 0){
        return(true);
    }else{
        return(false);
    }
}

for

function forLoopTest() public pure returns(uint256){
    uint sum = 0;
    for(uint i = 0; i < 10; i++){
        sum += i;
    }
    return(sum);
}

while

function whileTest() public pure returns(uint256){
    uint sum = 0;
    uint i = 0;
    while(i < 10){
        sum += i;
        i++;
    }
    return(sum);
}

do-while

function doWhileTest() public pure returns(uint256){
    uint sum = 0;
    uint i = 0;
    do{
        sum += i;
        i++;
    }while(i < 10);
    return(sum);
}

三元运算符

三元運算子是Solidity中唯一一個接受三個運算元的運算符,規則条件? 条件为真的表达式:条件为假的表达式。此運算子經常用作if語句的捷徑。

// 三元运算符 ternary/conditional operator
function ternaryTest(uint256 x, uint256 y) public pure returns(uint256){
    // return the max of x and y
    return x >= y ? x: y; 
}

另外還有 continue(立即進入下一個循環)和 break(跳出目前循環)關鍵字可以使用。

90%以上的人用Solidity寫插入演算法都會出錯。

排序: Solidity中最常用的變數類型是 uint,也就是正整數,取到負值的話,會報 underflow 錯誤。而在插入演算法中,變數j有可能會取到-1,造成報錯。

這裡,我們需要把j加1,讓它無法取到負值。正確代碼:

// 插入排序 正确版
function insertionSort(uint[] memory a) public pure returns(uint[] memory) {
    // note that uint can not take negative value
    for (uint i = 1;i < a.length;i++){
        uint temp = a[i];
        uint j=i;
        while( (j >= 1) && (temp < a[j-1])){
            a[j] = a[j-1];
            j--;
        }
        a[j] = temp;
    }
    return(a);
}

11. 构造函数和修饰器

构造函数

合約部署的時候第一個做的事情,如下初始化 owner;

address public owner;

constructor() public {
    owner = msg.sender;
}

修饰器 modifier

如下:

// 定义modifier
modifier onlyOwner {
   require(msg.sender == owner); // 检查调用者是否为owner地址
   _; // 如果是的话,继续运行函数主体;否则报错并revert交易
}

配合
function changeOwner(address _newOwner) external onlyOwner{
   owner = _newOwner; // 只有owner地址运行这个函数,并改变owner
}