基于以太坊的智能合约开发Solidity(内存&结构体篇)

2023-12-13 06:50:23

参考教程:【内存、引用与持久化存储】1、内存与区块链——storage与memory原理_哔哩哔哩_bilibili

1、storage与memory:

pragma solidity ^0.5.17;

contract MemoryTest
{
uint z = 1; ? //这是在合约中定义的状态变量,它会永久地(随本合约)存储在区块链上,也就是storage中,直至合约被销毁

? ? function add(uint num) public view returns(uint)
? ? {
? ? ? ? num += 1; ?//对函数形参进行修改,但是函数形参仅存储在内存,也就是memory,当函数执行完成,形参随之被销毁
? ? ? ? 
? ? ? ? return num;
? ? }
? ? 
? ? function test() public view returns(uint,uint)
? ? {
? ? ? ? uint i = 2; ?//这是在函数内部定义的变量,也存储在内存memory中,当函数执行完成也会被销毁
? ? ? ? 
? ? ? ? uint j = ?add(i); ?//把i作为参数传入add函数中,add函数会为i建立副本,在add中对生成的形参副本进行修改,不会影响i本身的值
? ? ? ? 
? ? ? ? return(i,j);
? ? }
}

(1)所有的复杂类型,即数组、结构和映射类型,都有一个额外属性——“数据位置”,用来说明数据是保存在内存memory中还是存储storage中,保存在memory中的数据,在函数执行完毕后空间会被释放,而保存在storage中的数据会随合约一直存储在区块链上。

(2)根据上下文不同,大多数时候数据有默认的位置,但也可以通过在类型名后增加关键字storage或 memory 进行修改。

(3)函数参数(包括返回的参数)的数据位置默认是 memory,局部变量的数据位置默认是memory,状态变量的数据位置强制是storage。

(4)另外还存在第三种数据位置——calldata ,这是一块只读的,且不会永久存储的位置,用来存储函数参数;外部函数的参数(非返回参数)的数据位置被强制指定为 calldata ,效果跟 memory 差不多。

(5)公开可见(publicly visible)的函数参数一定是 memory 类型,如果要求是 storage 类型 则必须是 private 或者 internal 函数,这是为了防止随意的公开调用占用资源。

2、storage引用:

pragma solidity ^0.5.17;

contract StorageTest
{
? ? 
? ? uint[] arrx; ?//这个变量定义在storage中,也就是随合约写在区块链中
? ? 
? ? function test(uint[] memory arry) public returns(uint)  //用memory修饰的变量,定义在内存中,它可以在函数体内部正常使用,和一般的变量没多少区别
? ? {
? ? ? ? arrx = arry; ? //把内存中的arry赋给区块链中的arrx,arrx会被改变
? ? ? ? 
? ? ? ? uint[] storage z = arrx; ?//在函数体内部定义一个可变长度的数组时,若声明是storage类型(该版本编译器没有默认storage,必须声明)
? ? ? ? //它就相当于一个指针(或者C++中的引用),指向区块链上的arrx,当修改z的时候,实际上操作的是区块链上的arrx(仅限于数组、mapping类型和结构体有这种语法)
? ? ? ? 
? ? ? ? z[0] = 100; ?//实际上修改了区块链上的arrx
? ? ? ? 
? ? ? ? z.length = 100; ?//实际上修改了区块链上arrx的长度
? ? ? ? 
? ? ? ? return z[0];
}

? ? // 返回arrx的第一个元素
? ? function test2() public returns(uint)
? ? {
? ? ? ? return arrx[0];
? ? }
? ? 
? ? // 返回arrx的长度
? ? function test3() public returns(uint)
? ? {
? ? ? ? return arrx.length;
? ? }
? ? ? ? 
}

3、结构体:

(1)定义及初始化:

pragma solidity ^0.5.17;

contract StructTest
{
? ? //定义一个结构体(在合约内部定义)
? ? struct Student
? ? {
? ? ? ? string name;
? ? ? ? uint grade;
? ? ? ? //Student student; ?与其它语言一样,禁止结构体内部包含自己(否则创建结构体时会无限开辟空间)
? ? ? ? //Student[] student; ?不过结构体中可以定义自己的动态长度数组,其初始长度为0,不会无限开辟空间
? ? ? ? //mapping(uint=>Student) Map; ?//通过mapping也可以包含自己
? ? }
? ? ? ? 
? ? function init() public view returns(string memory,uint)
? ? {
? ? ? ? // 初始化方式一
? ? ? ? Student memory s = Student("lalala",100); ?
? ? ? ? //函数体内部创建结构体必须加memory,否则会认为这是创建一个指向storage中结构体的指针,会报错(动态长度数组同理)
? ? ? ? return(s.name,s.grade);
? ? }
? ? ? ? 
? ? function init2() public view returns(string memory,uint)
? ? {
? ? ? ? // 初始化方式二 ? 
? ? ? ? Student memory s = Student({name:"lalala",grade:100});
? ? ? ? //在初始化结构体时可以带上变量的名称
? ? ? ? return(s.name,s.grade);
? ? }
? ? 
}

(2)mapping特性:

pragma solidity ^0.5.17;

contract StructTest
{
? ? struct Student
? ? {
? ? ? ? string name;
? ? ? ? uint grade;
? ? ? ? mapping(uint=>string) Map; ?
? ? }
? ? 
Student s2;

? ? function init() public returns(string memory,uint)
? ? {
? ? ? ? //结构体中存在mapping时,初始化结构体可以忽视mapping
? ? ? ? Student memory s = Student("lalala",100);

? ? ? ? //但是memory类型结构体对象是不能直接操作mapping属性变量的
? ? ? ? // s.Map[0] = "wawawa";

? ? ? ? //这时可以在函数体外部创建一个变量,把内存中的s复制给外部的变量,通过外部变量进行操作
? ? ? ? s2 = s;
? ? ? ? s2.Map[0] = "wawawa";

? ? ? ? return(s2.name,s2.grade);
? ? }
}

(3)结构体作为函数参数:

pragma solidity ^0.5.17;

contract StructTest
{
? ? struct Student
? ? {
? ? ? ? string name;
? ? ? ? uint grade;
? ? }
? ? 
? ? //结构体作为函数参数时,函数必须用internal修饰
? ? function test(Student memory student) internal
? ? {
? ? ? ? Student memory stu = student;  //结构体作为形参不能直接赋值给storage类型的结构体,除非形参中的结构体也用storage修饰
? ? }
}

4、结构体中storage和memory的类型转换:

(1)storage=>storage:

pragma solidity ^0.5.17;

contract StructTest
{
? ? struct Student
? ? {
? ? ? ? string name;
? ? ? ? string grade;
? ? }
? ? 
? ? Student student; ?//合约状态变量的类型为storage
? ? 
? ? function getStudent(Student storage stu) internal returns(Student memory)
? ? {
? ? ? ? Student storage stu1 = stu; ?//函数体内部定义指针,指向传入的函数形参,而函数形参stu又指向状态变量student(也可看作是C++中的引用)
? ? ? ? 
? ? ? ? stu1.name = "lalala";
? ? ? ? stu1.grade = "10000"; ?//通过stu1指针(也可以理解为C++中的引用)能修改状态变量student
? ? ? ? 
? ? ? ? return stu1;
? ? }
? ? 
? ? function test() public returns(string memory) 
? ? {
? ? ? ? return getStudent(student).name; ?//所调用的函数形参是storage类型,可以通过编译
? ? }
}

(2)memory=>storage:

pragma solidity ^0.5.17;

contract StructTest
{
? ? struct Student
? ? {
? ? ? ? string name;
? ? ? ? string grade;
? ? }
? ? 
? ? Student student;
? ? 
? ? function getStudent(Student memory stu) internal returns(Student memory)
? ? {
? ? ? ? student = stu; ?//直接将传进函数的结构体stu拷贝到状态变量student中
? ? ? ? 
? ? ? ? stu.name = "lalala"; ?//修改函数形参,对tmp以及student都不会有影响
? ? ? ? stu.grade = "100";

? ? ? ? //student = stu; ?如果在这里再进行拷贝,那么student就会受影响,因为是将修改后的stu拷贝到student中
? ? ? ? 
? ? ? ? return stu;
? ? }
? ? 
? ? function test() public returns(string memory) 
? ? {
? ? ? ? Student memory tmp = Student("wangxiaoer","60"); ?//在函数体内部创建结构体变量
? ? ? ? 
? ? ? ? getStudent(tmp); ?//把在内存中创建的结构体变量当作参数传入函数中
? ? ? ? ?
? ? ? ? return student.name;
? ? }
}

(3)storage=>memory:

pragma solidity ^0.5.17;

contract StructTest
{
? ? struct Student
? ? {
? ? ? ? string name;
? ? ? ? string grade;
? ? }
? ? 
? ? Student student = Student("wangxiaoer","60");
? ? 
? ? function getStudent(Student storage stu) internal returns(Student memory)
? ? {
? ? ? ? Student memory student2 = stu; ?//把stu指向(或引用)的student的内容赋给内存中的student2
? ? ? ? 
? ? ? ? student2.name = "lalala"; ?//修改内存中的student2,不会影响storage中的student
? ? ? ? 
? ? ? ? student2.grade = "100";
? ? ? ? 
? ? ? ? return student2;
? ? }
? ? 
? ? function test() public returns(string memory) 
? ? {
? ? ? ? getStudent(student);
? ? ? ? ?
? ? ? ? return student.name;
? ? }
}

(4)memory=>memory:

pragma solidity ^0.5.17;

contract StructTest
{
? ? struct Student
? ? {
? ? ? ? string name;
? ? ? ? string grade;
? ? }
? ? 
? ? function getStudent(Student memory stu) internal returns(Student memory)
? ? {
? ? ? ? Student memory ter = stu; ?//stu是指向内存中meimei的指针,但它却是memory类型,所以ter也是指向meimei的指针
? ? ? ? 
? ? ? ? ter.name = "lalalalala"; ?//通过ter竟然可以修改meimei
? ? ? ? 
? ? ? ? ter.grade = "90";
? ? ? ? 
? ? ? ? return ter;
? ? }
? ? 
? ? function test() public returns(string memory) 
? ? {
? ? ? ? Student memory meimei = Student("meimei","3");
? ? ? ? 
? ? ? ? getStudent(meimei); ?//memory实参转给memory形参是指针指向(记住就好,不建议去理解)
? ? ? ? ?
? ? ? ? return meimei.name;
? ? }
}

5、枚举体:

pragma solidity ^0.5.17;

contract EnumTest
{
    enum grade{first,second,third} ?//定义枚举,first的值为0,second的值为1,以此类推

    grade mingming = grade.first; ?//创建枚举变量

? ? function getEnum() public view returns(grade)
? ? {
? ? ? ? return mingming; ?//返回值为uint8:0
    }

? ? function getEnum2() public view returns(grade)
? ? {
? ? ? ? return grade.second; ?//返回值为uint8:1
    }

}

文章来源:https://blog.csdn.net/Zevalin/article/details/134894254
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。