变量数据存储类型只修饰引用类型 有以下三种类型
- 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)是非常重要的数据结构之一。它们允许存储同一类型的多个元素。数组可以是定长数组(长度固定)或动态数组(长度可变),并且可以存储基本类型(如 uint
、bool
)或引用类型(如 struct
、mapping
)。
接下来将详细讲解 Solidity 中数组的用法和相关操作。
定长数组的大小在声明时就固定,无法更改。
// 声明一个包含 5 个 uint 元素的定长数组
uint[5] public fixedArray;
动态数组可以在运行时修改大小。
// 声明一个动态数组
uint[] public dynamicArray;
数组可以通过字面量来初始化:
// 初始化定长数组
uint[3] public fixedArray = [1, 2, 3];
// 初始化动态数组
uint[] public dynamicArray = [1, 2, 3];
可以使用 .length
属性获取数组的长度。
uint length = dynamicArray.length; // 返回数组长度
可以通过索引来访问数组的元素。索引从 0 开始。
uint element = dynamicArray[1]; // 获取第二个元素
可以通过索引来修改数组中的元素。
dynamicArray[1] = 10; // 修改第二个元素为 10
可以使用 push
方法向动态数组中添加新元素。
dynamicArray.push(4); // 向数组末尾添加 4
- 定长数组中,删除元素会将该索引处的值重置为默认值(
0
或false
等),但数组长度不会改变。 - 动态数组中,删除元素也不会缩短数组长度,且不会重排数组中的元素。如果想从数组中移除最后一个元素,可以使用
pop
方法。
delete dynamicArray[1]; // 将数组中索引为 1 的元素重置为 0
dynamicArray.pop(); // 移除数组中的最后一个元素
你可以通过 for
循环遍历数组中的元素。
for (uint i = 0; i < dynamicArray.length; i++) {
uint element = dynamicArray[i];
// 执行操作
}
Solidity 中的数组可以存储在两种不同的地方:storage
和 memory
。默认情况下,状态变量(比如存储在合约中的数组)是 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;
}
Solidity 还支持多维数组(数组的数组)。你可以声明、初始化和访问多维数组。
// 声明一个二维的动态数组
uint[][] public multiDimensionalArray;
// 向二维数组添加元素
multiDimensionalArray.push([1, 2, 3]); // 添加一个一维数组
multiDimensionalArray[0].push(4); // 在第一行数组中添加元素 4
// 访问二维数组的元素
uint element = multiDimensionalArray[0][1]; // 获取第一行第二个元素
array.push(x)
:为动态数组添加一个元素x
。array.pop()
:删除数组的最后一个元素。
dynamicArray.push(5); // 向数组中添加元素 5
dynamicArray.pop(); // 删除数组中的最后一个元素
Solidity 函数可以返回数组,但是只能返回内存数组,而不能直接返回存储数组。
function getArray() public view returns (uint[] memory) {
return dynamicArray;
}
删除数组将会重置整个数组,但它不会改变动态数组的长度。
delete dynamicArray; // 删除整个数组,所有元素重置为 0
- 数组越界:访问不存在的索引会导致运行时错误,所以在访问数组元素时要确保索引在合法范围内。
- Gas 消耗:动态数组的操作(尤其是
push
和pop
)涉及写入存储,因此会消耗较多的 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)是一种用户自定义的数据类型,可以将多种不同类型的数据组合在一起。这对于组织和管理复杂数据非常有用。通过结构体,我们可以创建具有多个字段的数据类型,并通过它们来定义和管理数据。
结构体的定义语法类似于其他编程语言。可以在合约内或合约外定义结构体。
// 在合约内定义结构体
contract MyContract {
struct Person {
string name;
uint age;
bool isStudent;
}
}
一旦定义了结构体类型,就可以声明变量并使用它。
contract MyContract {
// 定义结构体
struct Person {
string name;
uint age;
bool isStudent;
}
// 使用结构体定义状态变量
Person public person1;
// 使用结构体初始化数据
function createPerson() public {
person1 = Person("Alice", 25, true);
}
}
可以通过点操作符 .
来访问结构体中的字段。
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 字段
}
}
可以声明多个结构体实例,这些实例可以存储不同的数据。
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);
}
}
你可以使用结构体数组来存储多个相同类型的结构体实例。
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);
}
}
可以将映射和结构体结合使用,通过映射的键来存储和管理结构体。
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);
}
}
结构体可以被直接修改。可以通过点操作符来修改特定的字段。
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;
}
}
在 Solidity 中,结构体可以存储在 storage
或 memory
中:
- 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
}
}
结构体可以通过 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)
}
}
结构体可以嵌套,即一个结构体中可以包含另一个结构体。
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 中用于存储复杂数据的非常有用的工具。
- 你可以将不同类型的数据组合在一起形成一个新的数据类型。
- 结构体可以与数组、映射结合使用,并且可以嵌套以创建更复杂的数据结构。