Skip to content

Latest commit

 

History

History
519 lines (393 loc) · 13.9 KB

File metadata and controls

519 lines (393 loc) · 13.9 KB

变量数据存储和作用域

变量数据存储类型只修饰引用类型 有以下三种类型

  • storage,表示变量存储在链上
  • memory,表示变量存储在内存
  • calldata,表示变量存储在内存,且不可变

作用域

  • 状态变量,合约内函数外
  • 局部变量,函数内
  • 全局变量,不需要申明,属于关键字,比如msg.value,msg.sender
  • 全局变量特殊变量
    • 以太坊单位 , 1 wei = 1 ; 1 gwei = 1e9; 1 ether = 1e18
    • 时间单位 1 seconds = 1 ; 1 minutes = 60; 1 hours = 60*60 ;1 days = 60*60*24

引用类型

数组

在 Solidity 中,数组(Arrays)是非常重要的数据结构之一。它们允许存储同一类型的多个元素。数组可以是定长数组(长度固定)或动态数组(长度可变),并且可以存储基本类型(如 uintbool)或引用类型(如 structmapping)。

接下来将详细讲解 Solidity 中数组的用法和相关操作。

1. 声明数组

1.1 定长数组

定长数组的大小在声明时就固定,无法更改。

// 声明一个包含 5 个 uint 元素的定长数组
uint[5] public fixedArray;

1.2 动态数组

动态数组可以在运行时修改大小。

// 声明一个动态数组
uint[] public dynamicArray;

2. 初始化数组

数组可以通过字面量来初始化:

// 初始化定长数组
uint[3] public fixedArray = [1, 2, 3];

// 初始化动态数组
uint[] public dynamicArray = [1, 2, 3];

3. 数组操作

3.1 获取数组长度

可以使用 .length 属性获取数组的长度。

uint length = dynamicArray.length;  // 返回数组长度

3.2 访问元素

可以通过索引来访问数组的元素。索引从 0 开始。

uint element = dynamicArray[1];  // 获取第二个元素

3.3 修改元素

可以通过索引来修改数组中的元素。

dynamicArray[1] = 10;  // 修改第二个元素为 10

3.4 添加元素(仅适用于动态数组)

可以使用 push 方法向动态数组中添加新元素。

dynamicArray.push(4);  // 向数组末尾添加 4

3.5 删除元素

  • 定长数组中,删除元素会将该索引处的值重置为默认值(0false 等),但数组长度不会改变。
  • 动态数组中,删除元素也不会缩短数组长度,且不会重排数组中的元素。如果想从数组中移除最后一个元素,可以使用 pop 方法。
delete dynamicArray[1];  // 将数组中索引为 1 的元素重置为 0
dynamicArray.pop();      // 移除数组中的最后一个元素

3.6 遍历数组

你可以通过 for 循环遍历数组中的元素。

for (uint i = 0; i < dynamicArray.length; i++) {
    uint element = dynamicArray[i];
    // 执行操作
}

4. 内存数组

Solidity 中的数组可以存储在两种不同的地方:storagememory。默认情况下,状态变量(比如存储在合约中的数组)是 storage 类型,意味着它们存储在链上并且是持久的。临时数组可以声明为 memory 类型,这些数组只在函数调用期间存在,不会存储在链上。

function createMemoryArray() public pure returns (uint[] memory) {
    // 创建一个长度为 3 的内存数组
    uint[] memory memArray = new uint[](3);
    memArray[0] = 1;
    memArray[1] = 2;
    memArray[2] = 3;
    return memArray;
}

5. 多维数组

Solidity 还支持多维数组(数组的数组)。你可以声明、初始化和访问多维数组。

5.1 声明多维数组

// 声明一个二维的动态数组
uint[][] public multiDimensionalArray;

5.2 初始化和操作多维数组

// 向二维数组添加元素
multiDimensionalArray.push([1, 2, 3]);  // 添加一个一维数组
multiDimensionalArray[0].push(4);       // 在第一行数组中添加元素 4

// 访问二维数组的元素
uint element = multiDimensionalArray[0][1];  // 获取第一行第二个元素

6. 使用 array.push()array.pop() 操作

  • array.push(x):为动态数组添加一个元素 x
  • array.pop():删除数组的最后一个元素。
dynamicArray.push(5);   // 向数组中添加元素 5
dynamicArray.pop();     // 删除数组中的最后一个元素

7. 数组的高级用法

7.1 返回数组

Solidity 函数可以返回数组,但是只能返回内存数组,而不能直接返回存储数组。

function getArray() public view returns (uint[] memory) {
    return dynamicArray;
}

7.2 删除整个数组

删除数组将会重置整个数组,但它不会改变动态数组的长度。

delete dynamicArray;  // 删除整个数组,所有元素重置为 0

8. 注意事项

  • 数组越界:访问不存在的索引会导致运行时错误,所以在访问数组元素时要确保索引在合法范围内。
  • Gas 消耗:动态数组的操作(尤其是 pushpop)涉及写入存储,因此会消耗较多的 Gas 费。在处理大数组时要小心 Gas 的消耗。

示例代码

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ArrayExample {
    // 定义一个动态数组
    uint[] public dynamicArray;

    // 添加元素到数组
    function addElement(uint _value) public {
        dynamicArray.push(_value);
    }

    // 获取数组中的元素
    function getElement(uint _index) public view returns (uint) {
        require(_index < dynamicArray.length, "Index out of bounds");
        return dynamicArray[_index];
    }

    // 获取数组长度
    function getArrayLength() public view returns (uint) {
        return dynamicArray.length;
    }

    // 删除数组中的元素
    function deleteElement(uint _index) public {
        require(_index < dynamicArray.length, "Index out of bounds");
        delete dynamicArray[_index];  // 将指定索引的元素重置为默认值
    }

    // 删除最后一个元素
    function removeLastElement() public {
        require(dynamicArray.length > 0, "Array is empty");
        dynamicArray.pop();  // 移除最后一个元素
    }
}

结构体

在 Solidity 中,结构体(Struct)是一种用户自定义的数据类型,可以将多种不同类型的数据组合在一起。这对于组织和管理复杂数据非常有用。通过结构体,我们可以创建具有多个字段的数据类型,并通过它们来定义和管理数据。

1. 定义结构体

结构体的定义语法类似于其他编程语言。可以在合约内或合约外定义结构体。

// 在合约内定义结构体
contract MyContract {
    struct Person {
        string name;
        uint age;
        bool isStudent;
    }
}

2. 使用结构体

一旦定义了结构体类型,就可以声明变量并使用它。

contract MyContract {
    // 定义结构体
    struct Person {
        string name;
        uint age;
        bool isStudent;
    }

    // 使用结构体定义状态变量
    Person public person1;

    // 使用结构体初始化数据
    function createPerson() public {
        person1 = Person("Alice", 25, true);
    }
}

3. 结构体的字段访问

可以通过点操作符 . 来访问结构体中的字段。

contract MyContract {
    struct Person {
        string name;
        uint age;
        bool isStudent;
    }

    Person public person1;

    function createPerson() public {
        person1 = Person("Alice", 25, true);
    }

    function getPersonName() public view returns (string memory) {
        return person1.name;  // 访问结构体的 name 字段
    }

    function setPersonAge(uint _age) public {
        person1.age = _age;  // 修改结构体的 age 字段
    }
}

4. 声明多个结构体实例

可以声明多个结构体实例,这些实例可以存储不同的数据。

contract MyContract {
    struct Person {
        string name;
        uint age;
        bool isStudent;
    }

    Person public person1;
    Person public person2;

    function createPersons() public {
        person1 = Person("Alice", 25, true);
        person2 = Person("Bob", 30, false);
    }
}

5. 存储结构体的数组

你可以使用结构体数组来存储多个相同类型的结构体实例。

contract MyContract {
    struct Person {
        string name;
        uint age;
        bool isStudent;
    }

    // 声明一个结构体数组
    Person[] public people;

    // 向数组中添加新的结构体实例
    function addPerson(string memory _name, uint _age, bool _isStudent) public {
        people.push(Person(_name, _age, _isStudent));
    }

    // 获取数组中的某个结构体
    function getPerson(uint _index) public view returns (string memory, uint, bool) {
        Person memory person = people[_index];
        return (person.name, person.age, person.isStudent);
    }
}

6. 使用映射(mapping)存储结构体

可以将映射和结构体结合使用,通过映射的键来存储和管理结构体。

contract MyContract {
    struct Person {
        string name;
        uint age;
        bool isStudent;
    }

    // 定义一个映射,将地址与结构体关联
    mapping(address => Person) public people;

    // 通过地址添加一个新的结构体实例
    function addPerson(string memory _name, uint _age, bool _isStudent) public {
        people[msg.sender] = Person(_name, _age, _isStudent);
    }

    // 获取存储的结构体
    function getPerson() public view returns (string memory, uint, bool) {
        Person memory person = people[msg.sender];
        return (person.name, person.age, person.isStudent);
    }
}

7. 修改结构体

结构体可以被直接修改。可以通过点操作符来修改特定的字段。

contract MyContract {
    struct Person {
        string name;
        uint age;
        bool isStudent;
    }

    Person public person1;

    function createPerson() public {
        person1 = Person("Alice", 25, true);
    }

    // 修改结构体字段
    function updatePerson(string memory _name, uint _age, bool _isStudent) public {
        person1.name = _name;
        person1.age = _age;
        person1.isStudent = _isStudent;
    }
}

8. 内存和存储中的结构体

在 Solidity 中,结构体可以存储在 storagememory 中:

  • storage:永久存储在区块链上,状态变量默认是 storage 类型。
  • memory:临时存储,函数中的局部变量可以声明为 memory,它们在函数调用结束后就会被销毁。
contract MyContract {
    struct Person {
        string name;
        uint age;
        bool isStudent;
    }

    Person public person1;

    function createPerson() public {
        person1 = Person("Alice", 25, true);
    }

    // 使用 memory 关键字声明一个局部结构体
    function getPersonInMemory() public view returns (string memory, uint, bool) {
        Person memory tempPerson = person1;  // 将结构体复制到内存中
        tempPerson.age = 30;                 // 修改内存中的数据
        return (tempPerson.name, tempPerson.age, tempPerson.isStudent);  // 这不会影响链上存储的 person1
    }
}

9. 删除结构体

结构体可以通过 delete 关键字删除,删除后,结构体的所有字段会被重置为其默认值。

contract MyContract {
    struct Person {
        string name;
        uint age;
        bool isStudent;
    }

    Person public person1;

    function createPerson() public {
        person1 = Person("Alice", 25, true);
    }

    // 删除结构体
    function deletePerson() public {
        delete person1;  // 将 person1 的所有字段重置为默认值(字符串为空,数字为 0,布尔值为 false)
    }
}

10. 嵌套结构体

结构体可以嵌套,即一个结构体中可以包含另一个结构体。

contract MyContract {
    struct Address {
        string street;
        string city;
    }

    struct Person {
        string name;
        uint age;
        Address addressInfo;
    }

    Person public person1;

    function createPerson() public {
        person1 = Person("Alice", 25, Address("123 Main St", "New York"));
    }

    function getPersonAddress() public view returns (string memory, string memory) {
        return (person1.addressInfo.street, person1.addressInfo.city);
    }
}

示例代码

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract StructExample {
    struct Car {
        string make;
        string model;
        uint year;
    }

    // 创建一个动态数组来存储 Car 结构体
    Car[] public cars;

    // 添加一辆新车到数组
    function addCar(string memory _make, string memory _model, uint _year) public {
        cars.push(Car(_make, _model, _year));
    }

    // 获取某个索引处的车辆信息
    function getCar(uint _index) public view returns (string memory, string memory, uint) {
        require(_index < cars.length, "Index out of bounds");
        Car memory car = cars[_index];
        return (car.make, car.model, car.year);
    }

    // 更新某个索引处的车辆信息
    function updateCar(uint _index, string memory _make, string memory _model, uint _year) public {
        require(_index < cars.length, "Index out of bounds");
        Car storage car = cars[_index];
        car.make = _make;
        car.model = _model;
        car.year = _year;
    }
}

总结

  • 结构体是 Solidity 中用于存储复杂数据的非常有用的工具。
  • 你可以将不同类型的数据组合在一起形成一个新的数据类型。
  • 结构体可以与数组、映射结合使用,并且可以嵌套以创建更复杂的数据结构。