あなたもきっと、誰かの奇跡 (你也一定会是某个人的奇迹)
目录
一、Solidity的结构体
1.结构体的实例化
2.结构体的基本使用
访问和修改结构体成员
3.结构体的修饰符
二、Solidity的映射
1.映射的基本使用
查询和修改某个value值
三、Solidity的枚举
四、数组、结构体、映射的直接相互嵌套
1.没有数组嵌套数组
2.数组嵌套结构体
3.没有数组嵌套映射
4.结构体嵌套数组
5.结构体嵌套结构体
6.结构体嵌套映射
7.映射嵌套数组
8.映射嵌套结构体
9.映射嵌套映射
10、重要说明(间接嵌套)
五、关于枚举的嵌套
一、Solidity的结构体
结构体是一个引用数据类型,用于表示复合型数据
在结构体里面的数据我们称之为成员
结构体可以任何数据类型作为体内的成员,但是不能内部包含自身结构体,也就是不能在自己的结构体中写自己的结构体
结构体可以多次实例化,且各个实例化互不影响
关键字:struct
定义结构体格式:
struct 结构体名 {
成员1;
成员2;
成员3;
```````````
}
结构体的可见性:
关于可见性,目前只支持internal,所以结构体只能在合约内部和子合约内使用。包含结构体的函数必须显性声明为internal
因此结构体里面的成员也是internal,不能再定义权限修饰符了
1.结构体的实例化
结构体创建后其实是一个模板类似于java中的类,对其实例化以后,相当于拿了这个模板去使用,同一个结构体不同实例化互不影响,自己存储在自己对应的实例化内存中,类似于java的对象
实例化格式:
结构体名 修饰符 实例化结构体名
温馨提示:结构体实例化之后,会给实例化结构体内部数据赋予一个初始值
结构体实例化后整体赋值:
实例化名 = 结构体名(值1,值2,·····)
注意:上下这两种方式是整体赋值必须全部赋值,否则报错
实例化结构体并初始化格式:
结构体名 修饰符 实例化名 = 结构体名(值1,值2,·····) 这个要按成员顺序赋值
结构体名 修饰符 实例化名 = 结构体名({ 成员:值,成员:值,···}) 这个可以不按照顺序赋值
2.结构体的基本使用
访问和修改结构体成员
访问结构体成员格式:
实例化结构体名.成员
修改结构体成员格式:
实例化结构体名.成员 = 新值
3.结构体的修饰符
结构体实例化的修饰符是用于修饰它所存储的位置用memory/storage或者方位权限public````等
如果在函数外实例化,默认强制存储storage不需要写修饰符
如果在函数内实例化,就必须写所存储的位置
当结构体中存在mapping时,不允许再用memory创建和初始化,要用storage(因为maaping只能存储在storage中)
结构体中的mapping类型,可初始化,也可不初始化,其他类型不可
要对结构体中的mapping操作,只能通过storage的存储来操作
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract test {
struct Student {
string name;
int age;
}
Student public stu;
function set() public returns(string memory,int){
stu = Student(unicode'大哥',2);
stu.age=100;
return (stu.name,stu.age);//大哥,100
}
}
二、Solidity的映射
映射 mapping 是智能合约中很常用的一种数据类型,它是引用类型。
Solidity 映射 mapping 用于以键值对的形式存储数据
键 = key 值 = value
一个key对应者一个value
它与使用结构体不同,和数组类似,创建后不需要实例化,直接使用
- 在mapping中key可以是整型、字符串等基本数据类型,但不是引用数据类型和枚举
- 而value的类型没有数据类型限制,可以是任意数据类型,甚至使用一个mapping作为value也是允许的,
温馨提示:mapping不能作为参数使用,也不能返回整个mapping,只能返回key对应的value
关键字: mapping
映射的定义格式:
mapping(key数据类型 => value数据类型) 权限修饰符 映射名
注意
- 映射的数据位置只能是 storage,通常用于状态变量。所以它不可以定义存储修饰符
- 映射可以标记权限修饰符,当权限修饰符为 public, Solidity 会创建一个 getter 函数。 key数据 将成为 getter 的必须参数,并且 getter 会返回 key数据对应的value
映射是没有长度的,也没有 key 的集合或 value 的集合的概念。映射只能是存储的数据位置,因此只允许作为状态变量或作为函数内的存储引用 或 作为库函数的参数。 它们不能用于合约公有函数的参数或返回值。
1.映射的基本使用
查询和修改某个value值
在映射中我们只能查询到value的值,查询不到key的值,也就是说可以通过key查询value但不能通过value查询key
查询value格式:
映射名[key值]
修改value格式:
映射名[key值] = 新value值
初始化映射(删除)
delete 映射名[key值]
注意事项:在映射的查询中,如果赋予一个不存在(没有存储)的key值,那么它所对应和返回的value值就是value数据类型的默认值
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract test {
mapping(int=>int) public a;
function set() public returns(int){
a[1]=2;
return a[1];
}
function tests(int _b) public view returns(int){
return a[_b];
}
}
三、Solidity的枚举
solidity 的枚举类型 是一种用户自定义类型,用于表示多种状态。
枚举类型内部就是一个自定义的整型,默认的类型为
uint8
,当枚举数足够多时,它会自动变成uint16
。(也就是枚举里面存放的元素个数)枚举类型可以与整数进行显式转换,但不能进行隐式转换。显示转换会在运行时检查数值范围,如果不匹配,将会引起异常。
关键字:enum
定义格式:
enum 枚举名 { 枚举元素1,枚举元素2,枚举元素3,····· }
注意,枚举的结尾}不需要加;
定义枚举类型变量:
枚举名 权限修饰符 枚举变量名
枚举类型的变量用于存放枚举里面的某个元素值,默认的值为第一个枚举元素值
给枚举变量赋值:
第一种:枚举内元素赋值
枚举变量名 = 枚举名.枚举元素
第二种:自定义uint类型数据赋值
枚举变量名 = uint类型数据
注意:所给枚举变量赋值的uint类型数据必须是枚举元素内含有的值(枚举元素1-枚举元素n的uint值范围)不是该范围则运行报错
重置枚举变量:将其重置为枚举元素1的值
关键字:delete
格式:
delete 枚举变量名
枚举体语法格式注释:
1)enum必须要有成员对象, { }中不能为空;
2)enum 中不能出现中文;
3){ }中不能加分号使用枚举类型的主要好处:
明确值的范围,防止错误的值输入。
提高代码的可读性,使得代码更加清晰易懂。
便于维护,需要增加/删除枚举类型的值的时候,只需要修改枚举类型的定义,不需要修改使用枚举类型的代码。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract EnumExample {
enum Status {
Pending,
Shipped,
Accepted,
Rejected,
Canceled
//枚举类型是unit8数据结构,内部元素均为uint8由0开始按顺序赋值
//若元素超出了uint8赋值则增加为uint16
// 枚举元素 对应的uint
// Pending --- 0
// Shipped --- 1
// Accepted --- 2
// Rejected --- 3
// Canceled --- 4
}
//定义了枚举类型的变量
Status public status;
function get() public view returns (Status) {
return status; //默认分配Pending 0
}
function set1() public {
status = Status.Canceled; //设置枚举元素Canceled 4
}
function set(Status _a) public {
status = _a; //外部uint值类型 范围 0-4 ,不是该范围则运行报错
}
function reset() public {
delete status;
}
}
四、数组、结构体、映射的直接相互嵌套
1.没有数组嵌套数组
数组不能嵌套数组,只能数组和数组之间相互赋值
2.数组嵌套结构体
在数组里面存放结构体,我们称之为数组结构体
定义格式:
结构体名[长度] 修饰符 数组名
提示:长度可写可不写,写了代表定长数组,不写代表变长数组,变长数组要注意一开始没有长度,需要push()添加值(变长)
查询一个下标对应整个结构体格式:
数组名[下标]
查询一个下标对应结构体的成员值格式:
数组名[下标].结构体成员名
赋值就在查询后面加个 =
作为函数参数的注意事项
当返回的是一个数组结构体值,那么返回的参数也必须是数组结构体
当数组结构体作为形参,那么传入的实参也必须是数组结构体
3.没有数组嵌套映射
数组不能嵌套映射,只能数组和映射相互赋值
4.结构体嵌套数组
在结构体里面让数组作为结构体成员,我们称之为结构体数组
结构体数组可以是变长的也可以是定长的
结构体数组定义格式:
struct 结构体名 {
数据类型[长度] 权限修饰符 数组名;
}
无法给结构体数组初始化赋值
访问结构体数组:
实例化结构体名.数组名
访问结构体数组的数组元素
实例化结构体名.数组名[下标]
给某个实例化结构体中的映射赋值
实例化结构体名.数组名[下标] = 元素值
如果是变长数组则是push()赋值
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Example {
struct aaa {
string name;
int age;
int[2] arr;
int[] arr2;
}
aaa public a;
function set() public {
a.name=unicode'a';
a.age=19;
a.arr[0]=123;
a.arr2.push(10);
}
function get() public view returns(int,int){
return(a.arr[0],a.arr2[0]);
}
function getArr() public view returns(int[2] memory){
return a.arr;
}
}
5.结构体嵌套结构体
结构体嵌套结构体,我们称之为结构体结构体,嵌套的里面的结构体是实例化的结构体
结构体结构体定义格式:
struct 结构体名1 {
成员;
·····
}
struct 结构体名 {
实例化结构体1
}
访问结构体结构体:
实例化结构体名.实例化结构体1
访问结构体数组的数组元素
实例化结构体名.实例化结构体1.实例化结构体1的成员
赋值就后面加 =
6.结构体嵌套映射
在结构体里面嵌套映射作为结构体的成员,我们成之为结构体映射
注意事项:
- 当结构体中存在mapping时,不允许再用memory对结构体实例化,要用storage(因为maaping只能存储在storage中)
- 要对结构体中的mapping操作,只能通过storage的存储来操作
- 在结构体里面的映射不能加权限修饰符,它强制和struct一样的权限
结构体映射定义格式:
struct 结构体名 {
mapping(key类型 => value类型) 映射名;
}
对结构体实例化并初始化mapping
以大括号引出,不按顺序赋值结构体名 别名= 结构体名( { key名:值,value名:值 } )
实例化结构体后对mapping初始化
以大括号引出,按顺序赋值结构体名: 实例化= 结构体名( { key值,value值 } )
访问结构体种的映射value值
实例化结构体名.映射名[kye值]
给某个实例化结构体中的映射赋值
实例化结构体名.映射名[key值] = value值
作为函数参数的注意事项
当返回的是一个数组结构映射,那么返回的参数也必须是结构体映射
当结构体映射作为形参,那么传入的实参也必须是结构体
7.映射嵌套数组
映射内的value作为数组,整个数组被映射嵌套,我们称之为映射数组
定义格式:
mapping(key类型 => 数组类型[长度]) 权限修饰符 映射名
访问映射里面存储数组
映射名[key值]
访问映射里面存储的数组具体值
映射名[key值][下标]
赋值就在查询后面加个 =
注意事项:映射本身固定为storage类型,因此使用的数组也必须是storage类型
如果里面是动态数组,则需要使用push()来赋值,格式:
映射名[key值].push(值)
8.映射嵌套结构体
映射内的value作为结构体,整个结构体被映射嵌套,我们称之为映射结构体
定义格式:
mapping(key类型 => 结构体名) 权限修饰符 映射名
访问映射里面存储的某个实例化结构体
映射名[key值]
访问映射里面存储的某个实例化结构体成员
映射名[key值].结构体成员
赋值就在查询后面加个 =
注意事项:映射本身固定为storage类型,因此使用的结构体也必须是storage类型
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Example {
struct aaa {
string name;
int age;
}
mapping(int=>aaa) public bbb;
aaa public a;
function get(string memory _name,int _age,int _id) public returns(aaa memory,int) {
a.name=_name;
a.age=_age;
bbb[_id]=a;
return (bbb[_id],bbb[_id].age);
}
}
9.映射嵌套映射
映射内的value作为映射,整个内部映射被外部映射嵌套,我们称之为映射映射
定义格式:
mapping(key类型 => mapping(key类型=>value类型)) 权限修饰符 映射名l
例如
mapping(int=>mapping(int=>string)) public a;
访问映射里面存储的映射
映射名[外部key值]
访问映射里面存储的映射的value值
映射名[外部key值][内部key值]
赋值就在查询后面加个 =
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SolidityTest {
mapping(int=>mapping(int=>string)) public a;
function tran() public returns(string memory) {
a[1][2]='a';
a[1];
return a[1][2];
}
}
10、重要说明(间接嵌套)
虽然有些不能直接嵌套,但是可以通过结构体作为媒介进行间接嵌套
而结构体可以通过数组和映射作为媒介间接嵌套
当然有些能直接前嵌套也能间接嵌套
例如
数组-->结构体-->数组
映射-->结构体-->映射
映射--结构体-->数组
数组--结构体-->映射
结构体 -->数组 -->结构体
结构体 -->映射 --> 结构体
五、关于枚举的嵌套
枚举通常都是作为枚举变量使用
枚举只能以枚举变量的形式嵌套在结构体内
使用枚举变量的注意事项:
1.枚举变量的赋值只能给枚举类型变量赋值,或枚举元素
2.枚举变量不能存放到数组、结构体、映射、普通变量中去,因为数据类型不一样
3.枚举变量可以定义到结构体里面,然后对结构体内的枚举变量赋予枚举变量(间接性)
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract Enum{
enum Status {
None,
Padding,
Shipped,
Completed,
Rejected,
Canceled
}
Status public status;//枚举变量
struct Order {
address buyer;
Status status;//结构体嵌套枚举
}
Order[] public orders;//数组结构体
function get() view external returns (Status) {
return status;//返回枚举变量
}
function set(Status _status) external {
status = _status;//枚举变量的赋值
}
function ship() external {
status = Status.Shipped;//枚举变量的赋值
}
function reset() external{
delete status;//重置枚举变量
}
}